/**
 * Login with Email Use Case
 *
 * Authenticates a user with email and password.
 */

import type { Logger } from '@core/shared/domain/Logger';
import { Result } from '@core/shared/domain/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import { UserRepository } from '../../domain/repositories/UserRepository';
import { PasswordHash } from '../../domain/value-objects/PasswordHash';
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';

export type LoginWithEmailInput = {
  email: string;
  password: string;
};

export type LoginWithEmailResult = {
  sessionToken: string;
  userId: string;
  displayName: string;
  email?: string;
  primaryDriverId?: string;
  issuedAt: number;
  expiresAt: number;
};

export type LoginWithEmailErrorCode =
  | 'INVALID_INPUT'
  | 'INVALID_CREDENTIALS'
  | 'REPOSITORY_ERROR';

export type LoginWithEmailApplicationError = ApplicationErrorCode<
  LoginWithEmailErrorCode,
  { message: string }
>;

export class LoginWithEmailUseCase {
  constructor(
    private readonly userRepository: UserRepository,
    private readonly sessionPort: IdentitySessionPort,
    private readonly logger: Logger,
  ) {}

  async execute(input: LoginWithEmailInput): Promise<Result<LoginWithEmailResult, LoginWithEmailApplicationError>> {
    try {
      if (!input.email || !input.password) {
        return Result.err({
          code: 'INVALID_INPUT',
          details: { message: 'Email and password are required' },
        } as LoginWithEmailApplicationError);
      }

      const normalizedEmail = input.email.toLowerCase().trim();

      const user = await this.userRepository.findByEmail(normalizedEmail);
      if (!user) {
        return Result.err({
          code: 'INVALID_CREDENTIALS',
          details: { message: 'Invalid email or password' },
        } as LoginWithEmailApplicationError);
      }

      // Verify password using PasswordHash value object
      const storedPasswordHash = PasswordHash.fromHash(user.passwordHash);
      const isValid = await storedPasswordHash.verify(input.password);
      
      if (!isValid) {
        return Result.err({
          code: 'INVALID_CREDENTIALS',
          details: { message: 'Invalid email or password' },
        } as LoginWithEmailApplicationError);
      }

      type CreateSessionInput = Parameters<IdentitySessionPort['createSession']>[0];

      const createSessionInput = {
        id: user.id,
        displayName: user.displayName,
        ...(user.email !== undefined ? { email: user.email } : {}),
        ...(user.primaryDriverId !== undefined
          ? { primaryDriverId: user.primaryDriverId }
          : {}),
      } satisfies CreateSessionInput;

      const session = await this.sessionPort.createSession(createSessionInput);

      const result: LoginWithEmailResult = {
        sessionToken: session.token,
        userId: session.user.id,
        displayName: session.user.displayName,
        ...(session.user.email !== undefined ? { email: session.user.email } : {}),
        ...(session.user.primaryDriverId !== undefined
          ? { primaryDriverId: session.user.primaryDriverId }
          : {}),
        issuedAt: session.issuedAt,
        expiresAt: session.expiresAt,
      };

      return Result.ok(result);
    } catch (error) {
      const message =
        error instanceof Error && error.message
          ? error.message
          : 'Failed to execute LoginWithEmailUseCase';

      this.logger.error(
        'LoginWithEmailUseCase.execute failed',
        error instanceof Error ? error : undefined,
        { input },
      );

      return Result.err({
        code: 'REPOSITORY_ERROR',
        details: { message },
      } as LoginWithEmailApplicationError);
    }
  }

}