/**
 * @ai-summary Fastify route handlers for auth API
 * @ai-context HTTP request/response handling with Fastify reply methods
 */

import { FastifyRequest, FastifyReply } from 'fastify';
import { AuthService } from '../domain/auth.service';
import { UserProfileRepository } from '../../user-profile/data/user-profile.repository';
import { TermsAgreementRepository } from '../../terms-agreement/data/terms-agreement.repository';
import { termsConfig } from '../../terms-agreement/domain/terms-config';
import { pool } from '../../../core/config/database';
import { logger } from '../../../core/logging/logger';
import { signupSchema, resendVerificationPublicSchema } from './auth.validation';
import { auditLogService } from '../../audit-log';

export class AuthController {
  private authService: AuthService;

  constructor() {
    const userProfileRepository = new UserProfileRepository(pool);
    const termsAgreementRepository = new TermsAgreementRepository(pool);
    this.authService = new AuthService(userProfileRepository, termsAgreementRepository);
  }

  /**
   * Extract client IP address from request
   * Checks X-Forwarded-For header for proxy scenarios (Traefik)
   */
  private getClientIp(request: FastifyRequest): string {
    const forwardedFor = request.headers['x-forwarded-for'];
    if (forwardedFor) {
      // X-Forwarded-For can be comma-separated list; first IP is the client
      const ips = Array.isArray(forwardedFor) ? forwardedFor[0] : forwardedFor;
      return ips.split(',')[0].trim();
    }
    return request.ip || 'unknown';
  }

  /**
   * POST /api/auth/signup
   * Create new user account
   * Public endpoint - no JWT required
   */
  async signup(request: FastifyRequest, reply: FastifyReply) {
    try {
      const validation = signupSchema.safeParse(request.body);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Validation error',
          message: validation.error.errors[0]?.message || 'Invalid input',
          details: validation.error.errors,
        });
      }

      const { email, password, termsAccepted } = validation.data;

      // Extract terms data for audit trail
      const termsData = {
        ipAddress: this.getClientIp(request),
        userAgent: (request.headers['user-agent'] as string) || 'unknown',
        termsVersion: termsConfig.version,
        termsUrl: termsConfig.url,
        termsContentHash: termsConfig.getContentHash(),
      };

      const result = await this.authService.signup({ email, password, termsAccepted }, termsData);

      logger.info('User signup successful', { email, userId: result.userId });

      // Log signup to unified audit log
      await auditLogService.info(
        'auth',
        result.userId,
        `User signup: ${email}`,
        'user',
        result.userId,
        { email, ipAddress: termsData.ipAddress }
      ).catch(err => logger.error('Failed to log signup audit event', { error: err }));

      return reply.code(201).send(result);
    } catch (error: any) {
      logger.error('Signup failed', { error, email: (request.body as any)?.email });

      if (error.message === 'Email already exists') {
        return reply.code(409).send({
          error: 'Conflict',
          message: 'An account with this email already exists',
        });
      }

      // Auth0 API errors
      if (error.message?.includes('Auth0')) {
        return reply.code(500).send({
          error: 'Internal server error',
          message: 'Failed to create account. Please try again later.',
        });
      }

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to create account',
      });
    }
  }

  /**
   * GET /api/auth/verify-status
   * Check email verification status
   * Protected endpoint - requires JWT
   */
  async getVerifyStatus(request: FastifyRequest, reply: FastifyReply) {
    try {
      const userId = (request as any).user.sub;

      const result = await this.authService.getVerifyStatus(userId);

      logger.info('Verification status checked', { userId, emailVerified: result.emailVerified });

      return reply.code(200).send(result);
    } catch (error: any) {
      logger.error('Failed to get verification status', {
        error,
        userId: (request as any).user?.sub,
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to check verification status',
      });
    }
  }

  /**
   * POST /api/auth/resend-verification
   * Resend verification email
   * Protected endpoint - requires JWT
   */
  async resendVerification(request: FastifyRequest, reply: FastifyReply) {
    try {
      const userId = (request as any).user.sub;

      const result = await this.authService.resendVerification(userId);

      logger.info('Verification email resent', { userId });

      return reply.code(200).send(result);
    } catch (error: any) {
      logger.error('Failed to resend verification email', {
        error,
        userId: (request as any).user?.sub,
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to resend verification email',
      });
    }
  }

  /**
   * POST /api/auth/resend-verification-public
   * Resend verification email by email address
   * Public endpoint - no JWT required (for pre-login verification page)
   */
  async resendVerificationPublic(request: FastifyRequest, reply: FastifyReply) {
    try {
      const validation = resendVerificationPublicSchema.safeParse(request.body);
      if (!validation.success) {
        return reply.code(400).send({
          error: 'Validation error',
          message: validation.error.errors[0]?.message || 'Invalid input',
        });
      }

      const { email } = validation.data;

      const result = await this.authService.resendVerificationByEmail(email);

      logger.info('Public resend verification requested', { email: email.substring(0, 3) + '***' });

      return reply.code(200).send(result);
    } catch (error: any) {
      logger.error('Failed to resend verification email (public)', { error });

      // Always return success for security (don't reveal if email exists)
      return reply.code(200).send({
        message: 'If an account exists with this email, a verification link will be sent.',
      });
    }
  }

  /**
   * GET /api/auth/user-status
   * Get user status for routing decisions
   * Protected endpoint - requires JWT
   *
   * Note: This endpoint is called once per Auth0 callback (from CallbackPage/CallbackMobileScreen).
   * We log the login event here since it's the first authenticated request after Auth0 redirect.
   */
  async getUserStatus(request: FastifyRequest, reply: FastifyReply) {
    try {
      const userId = (request as any).user.sub;

      const result = await this.authService.getUserStatus(userId);

      // Log login event to audit trail (called once per Auth0 callback)
      const ipAddress = this.getClientIp(request);
      await auditLogService.info(
        'auth',
        userId,
        'User login',
        'user',
        userId,
        { ipAddress }
      ).catch(err => logger.error('Failed to log login audit event', { error: err }));

      logger.info('User status retrieved', {
        userId: userId.substring(0, 8) + '...',
        emailVerified: result.emailVerified,
        onboardingCompleted: result.onboardingCompleted,
      });

      return reply.code(200).send(result);
    } catch (error: any) {
      logger.error('Failed to get user status', {
        error,
        userId: (request as any).user?.sub,
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to get user status',
      });
    }
  }

  /**
   * GET /api/auth/security-status
   * Get user security status
   * Protected endpoint - requires JWT
   */
  async getSecurityStatus(request: FastifyRequest, reply: FastifyReply) {
    try {
      const userId = (request as any).user.sub;

      const result = await this.authService.getSecurityStatus(userId);

      logger.info('Security status retrieved', {
        userId: userId.substring(0, 8) + '...',
        emailVerified: result.emailVerified,
      });

      return reply.code(200).send(result);
    } catch (error: any) {
      logger.error('Failed to get security status', {
        error,
        userId: (request as any).user?.sub,
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to get security status',
      });
    }
  }

  /**
   * POST /api/auth/request-password-reset
   * Request password reset email
   * Protected endpoint - requires JWT
   */
  async requestPasswordReset(request: FastifyRequest, reply: FastifyReply) {
    try {
      const userId = (request as any).user.sub;

      const result = await this.authService.requestPasswordReset(userId);

      logger.info('Password reset email requested', {
        userId: userId.substring(0, 8) + '...',
      });

      // Log password reset request to unified audit log
      await auditLogService.info(
        'auth',
        userId,
        'Password reset requested',
        'user',
        userId
      ).catch(err => logger.error('Failed to log password reset audit event', { error: err }));

      return reply.code(200).send(result);
    } catch (error: any) {
      logger.error('Failed to request password reset', {
        error,
        userId: (request as any).user?.sub,
      });

      return reply.code(500).send({
        error: 'Internal server error',
        message: 'Failed to send password reset email. Please try again later.',
      });
    }
  }

  /**
   * POST /api/auth/track-logout
   * Track user logout event for audit logging
   * Protected endpoint - requires JWT
   *
   * Called by frontend before Auth0 logout to capture the logout event.
   * Returns success even if audit logging fails (non-blocking).
   */
  async trackLogout(request: FastifyRequest, reply: FastifyReply) {
    try {
      const userId = (request as any).user.sub;
      const ipAddress = this.getClientIp(request);

      // Log logout event to audit trail
      await auditLogService.info(
        'auth',
        userId,
        'User logout',
        'user',
        userId,
        { ipAddress }
      ).catch(err => logger.error('Failed to log logout audit event', { error: err }));

      logger.info('User logout tracked', {
        userId: userId.substring(0, 8) + '...',
      });

      return reply.code(200).send({ success: true });
    } catch (error: any) {
      // Don't block logout on audit failure - always return success
      logger.error('Failed to track logout', {
        error,
        userId: (request as any).user?.sub,
      });

      return reply.code(200).send({ success: true });
    }
  }
}
