import { EmailAddress } from '../../domain/value-objects/EmailAddress';
import { IAuthRepository } from '../../domain/repositories/IAuthRepository';
import { IMagicLinkRepository } from '../../domain/repositories/IMagicLinkRepository';
import { IMagicLinkNotificationPort } from '../../domain/ports/IMagicLinkNotificationPort';
import { Result } from '@core/shared/application/Result';
import type { ApplicationErrorCode } from '@core/shared/errors/ApplicationErrorCode';
import type { UseCaseOutputPort, Logger, UseCase } from '@core/shared/application';
import { randomBytes } from 'crypto';

export type ForgotPasswordInput = {
  email: string;
};

export type ForgotPasswordResult = {
  message: string;
  magicLink?: string | null; // For development/demo purposes
};

export type ForgotPasswordErrorCode = 'USER_NOT_FOUND' | 'REPOSITORY_ERROR' | 'RATE_LIMIT_EXCEEDED';

export type ForgotPasswordApplicationError = ApplicationErrorCode<ForgotPasswordErrorCode, { message: string }>;

/**
 * Application Use Case: ForgotPasswordUseCase
 *
 * Handles password reset requests by generating magic links.
 * In production, this would send an email with the magic link.
 * In development, it returns the link for testing purposes.
 */
export class ForgotPasswordUseCase implements UseCase<ForgotPasswordInput, void, ForgotPasswordErrorCode> {
  constructor(
    private readonly authRepo: IAuthRepository,
    private readonly magicLinkRepo: IMagicLinkRepository,
    private readonly notificationPort: IMagicLinkNotificationPort,
    private readonly logger: Logger,
    private readonly output: UseCaseOutputPort<ForgotPasswordResult>,
  ) {}

  async execute(input: ForgotPasswordInput): Promise<Result<void, ForgotPasswordApplicationError>> {
    try {
      // Validate email format
      const emailVO = EmailAddress.create(input.email);

      // Check if user exists
      const user = await this.authRepo.findByEmail(emailVO);
      
      // Check rate limiting (implement in repository) - even if user doesn't exist
      const rateLimitResult = await this.magicLinkRepo.checkRateLimit(input.email);
      if (rateLimitResult.isErr()) {
        return Result.err({
          code: 'RATE_LIMIT_EXCEEDED',
          details: { message: 'Too many reset attempts. Please try again later.' },
        });
      }

      // If user exists, generate magic link
      if (user) {
        // Generate secure token
        const token = this.generateSecureToken();
        
        // Set expiration (15 minutes)
        const expiresAt = new Date(Date.now() + 15 * 60 * 1000);

        // Store magic link
        await this.magicLinkRepo.createPasswordResetRequest({
          email: input.email,
          token,
          expiresAt,
          userId: user.getId().value,
        });

        // Generate magic link URL
        const magicLink = this.generateMagicLink(token);

        this.logger.info('[ForgotPasswordUseCase] Magic link generated', {
          email: input.email,
          userId: user.getId().value,
          expiresAt,
        });

        // Send notification via port
        await this.notificationPort.sendMagicLink({
          email: input.email,
          magicLink,
          userId: user.getId().value,
          expiresAt,
        });

        this.output.present({
          message: 'Password reset link generated successfully',
          magicLink: process.env.NODE_ENV === 'development' ? magicLink : null,
        });
      } else {
        // User not found - still return success for security (prevents email enumeration)
        this.logger.info('[ForgotPasswordUseCase] User not found, but returning success for security', {
          email: input.email,
        });

        this.output.present({
          message: 'If an account exists with this email, a password reset link will be sent',
          magicLink: null,
        });
      }

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

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

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

  private generateSecureToken(): string {
    // Generate 32-byte random token and convert to hex
    return randomBytes(32).toString('hex');
  }

  private generateMagicLink(token: string): string {
    const baseUrl = process.env.NEXT_PUBLIC_API_BASE_URL || 'http://localhost:3001';
    return `${baseUrl}/auth/reset-password?token=${token}`;
  }
}