import { Injectable, Inject, InternalServerErrorException } from '@nestjs/common';
import type { AuthenticatedUserDTO, AuthSessionDTO, SignupParams, LoginParams, IracingAuthRedirectResult, LoginWithIracingCallbackParams } from './dto/AuthDto';

// Core Use Cases
import { LoginUseCase } from '@core/identity/application/use-cases/LoginUseCase';
import { SignupUseCase } from '@core/identity/application/use-cases/SignupUseCase';
import { GetCurrentSessionUseCase } from '@core/identity/application/use-cases/GetCurrentSessionUseCase';
import { LogoutUseCase } from '@core/identity/application/use-cases/LogoutUseCase';
import { StartIracingAuthRedirectUseCase } from '@core/identity/application/use-cases/StartIracingAuthRedirectUseCase';
import { LoginWithIracingCallbackUseCase } from '@core/identity/application/use-cases/LoginWithIracingCallbackUseCase';

// Core Interfaces and Tokens
import { AUTH_REPOSITORY_TOKEN, PASSWORD_HASHING_SERVICE_TOKEN, LOGGER_TOKEN, IDENTITY_SESSION_PORT_TOKEN, USER_REPOSITORY_TOKEN } from './AuthProviders';
import type { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository';
import type { IPasswordHashingService } from '@core/identity/domain/services/PasswordHashingService';
import type { Logger } from "@core/shared/application";
import { IdentitySessionPort } from '@core/identity/application/ports/IdentitySessionPort';
import { UserId } from '@core/identity/domain/value-objects/UserId';
import { User } from '@core/identity/domain/entities/User';
import type { IUserRepository } from '@core/identity/domain/repositories/IUserRepository';
import { AuthenticatedUserDTO as CoreAuthenticatedUserDTO } from '@core/identity/application/dto/AuthenticatedUserDTO';

@Injectable()
export class AuthService {
  private readonly loginUseCase: LoginUseCase;
  private readonly signupUseCase: SignupUseCase;
  private readonly getCurrentSessionUseCase: GetCurrentSessionUseCase;
  private readonly logoutUseCase: LogoutUseCase;
  private readonly startIracingAuthRedirectUseCase: StartIracingAuthRedirectUseCase;
  private readonly loginWithIracingCallbackUseCase: LoginWithIracingCallbackUseCase;

  constructor(
    @Inject(AUTH_REPOSITORY_TOKEN) private authRepository: IAuthRepository,
    @Inject(PASSWORD_HASHING_SERVICE_TOKEN) private passwordHashingService: IPasswordHashingService,
    @Inject(LOGGER_TOKEN) private logger: Logger,
    @Inject(IDENTITY_SESSION_PORT_TOKEN) private identitySessionPort: IdentitySessionPort,
    @Inject(USER_REPOSITORY_TOKEN) private userRepository: IUserRepository, // Inject IUserRepository here
  ) {
    this.loginUseCase = new LoginUseCase(this.authRepository, this.passwordHashingService);
    this.signupUseCase = new SignupUseCase(this.authRepository, this.passwordHashingService);
    this.getCurrentSessionUseCase = new GetCurrentSessionUseCase(); // Doesn't have constructor parameters normally
    this.logoutUseCase = new LogoutUseCase(this.identitySessionPort);
    this.startIracingAuthRedirectUseCase = new StartIracingAuthRedirectUseCase();
    this.loginWithIracingCallbackUseCase = new LoginWithIracingCallbackUseCase();
  }

  private mapUserToAuthenticatedUserDTO(user: User): AuthenticatedUserDTO {
    return {
      userId: user.getId().value,
      email: user.getEmail() ?? '',
      displayName: user.getDisplayName() ?? '',
      // Map other fields as necessary
      iracingCustomerId: user.getIracingCustomerId() ?? undefined,
      primaryDriverId: user.getPrimaryDriverId() ?? undefined,
      avatarUrl: user.getAvatarUrl() ?? undefined,
    };
  }

  async getCurrentSession(): Promise<AuthSessionDTO | null> {
    this.logger.debug('[AuthService] Attempting to get current session.');
    const coreSession = await this.identitySessionPort.getCurrentSession();
    if (!coreSession) {
      return null;
    }

    const user = await this.userRepository.findById(coreSession.user.id); // Use userRepository to fetch full user
    if (!user) {
        // If session exists but user doesn't in DB, perhaps clear session?
        this.logger.warn(`[AuthService] Session found for user ID ${coreSession.user.id}, but user not found in repository.`);
        await this.identitySessionPort.clearSession(); // Clear potentially stale session
        return null;
    }

    const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(User.fromStored(user));

    return {
      token: coreSession.token,
      user: authenticatedUserDTO,
    };
  }

  async signupWithEmail(params: SignupParams): Promise<AuthSessionDTO> {
    this.logger.debug(`[AuthService] Attempting signup for email: ${params.email}`);
    const user = await this.signupUseCase.execute(params.email, params.password, params.displayName);
    
    // Create session after successful signup
    const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(user);
    const session = await this.identitySessionPort.createSession(authenticatedUserDTO as CoreAuthenticatedUserDTO);

    return {
      token: session.token,
      user: authenticatedUserDTO,
    };
  }

  async loginWithEmail(params: LoginParams): Promise<AuthSessionDTO> {
    this.logger.debug(`[AuthService] Attempting login for email: ${params.email}`);
    try {
      const user = await this.loginUseCase.execute(params.email, params.password);
      // Create session after successful login
      const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(user);
      const session = await this.identitySessionPort.createSession(authenticatedUserDTO as CoreAuthenticatedUserDTO);

      return {
        token: session.token,
        user: authenticatedUserDTO,
      };
    } catch (error) {
      this.logger.error(`[AuthService] Login failed for email ${params.email}:`, error instanceof Error ? error : new Error(String(error)));
      throw new InternalServerErrorException('Login failed due to invalid credentials or server error.');
    }
  }

  async startIracingAuthRedirect(returnTo?: string): Promise<IracingAuthRedirectResult> {
    this.logger.debug('[AuthService] Starting iRacing auth redirect.');
    // Note: The StartIracingAuthRedirectUseCase takes optional returnTo, but the DTO doesnt
    const result = await this.startIracingAuthRedirectUseCase.execute(returnTo);
    // Map core IracingAuthRedirectResult to AuthDto's IracingAuthRedirectResult
    return { redirectUrl: result.redirectUrl, state: result.state };
  }

  async loginWithIracingCallback(params: LoginWithIracingCallbackParams): Promise<AuthSessionDTO> {
    this.logger.debug(`[AuthService] Handling iRacing callback for code: ${params.code}`);
    const user = await this.loginWithIracingCallbackUseCase.execute(params); // Pass params as is
    
    // Create session after successful iRacing login
    const authenticatedUserDTO = this.mapUserToAuthenticatedUserDTO(user);
    const session = await this.identitySessionPort.createSession(authenticatedUserDTO as CoreAuthenticatedUserDTO);

    return {
      token: session.token,
      user: authenticatedUserDTO,
    };
  }

  async logout(): Promise<void> {
    this.logger.debug('[AuthService] Attempting logout.');
    await this.logoutUseCase.execute();
  }
}
