import {
  Controller,
  Post,
  Get,
  Patch,
  Delete,
  Body,
  HttpCode,
  HttpStatus,
  UseGuards,
  ValidationPipe,
  Ip,
  Headers,
  Param,
  Req,
} from '@nestjs/common';
import { AuthService } from './auth.service';
import { PasswordResetService } from './password-reset.service';
import { MFAService } from './mfa.service';
import { SessionService } from './session.service';
import { DeviceTrustService } from './device-trust.service';
import { BiometricAuthService } from './biometric-auth.service';
import { RegisterDto } from './dto/register.dto';
import { LoginDto } from './dto/login.dto';
import { RefreshTokenDto } from './dto/refresh-token.dto';
import { LogoutDto } from './dto/logout.dto';
import { UpdateProfileDto } from './dto/update-profile.dto';
import {
  RequestPasswordResetDto,
  ResetPasswordDto,
  VerifyEmailDto,
} from './dto/password-reset.dto';
import { VerifyMFACodeDto, EnableTOTPDto } from './dto/mfa.dto';
import { Public } from './decorators/public.decorator';
import { CurrentUser } from './decorators/current-user.decorator';
import { JwtAuthGuard } from './guards/jwt-auth.guard';

@Controller('api/v1/auth')
export class AuthController {
  constructor(
    private readonly authService: AuthService,
    private readonly passwordResetService: PasswordResetService,
    private readonly mfaService: MFAService,
    private readonly sessionService: SessionService,
    private readonly deviceTrustService: DeviceTrustService,
    private readonly biometricAuthService: BiometricAuthService,
  ) {}

  @Public()
  @Post('register')
  @HttpCode(HttpStatus.CREATED)
  async register(@Body(ValidationPipe) registerDto: RegisterDto) {
    return await this.authService.register(registerDto);
  }

  @Public()
  @Post('login')
  @HttpCode(HttpStatus.OK)
  async login(@Body(ValidationPipe) loginDto: LoginDto) {
    return await this.authService.login(loginDto);
  }

  @Public()
  @Post('refresh')
  @HttpCode(HttpStatus.OK)
  async refresh(@Body(ValidationPipe) refreshTokenDto: RefreshTokenDto) {
    return await this.authService.refreshAccessToken(refreshTokenDto);
  }

  @UseGuards(JwtAuthGuard)
  @Get('me')
  @HttpCode(HttpStatus.OK)
  async getMe(@CurrentUser() user: any) {
    return await this.authService.getUserById(user.userId);
  }

  @UseGuards(JwtAuthGuard)
  @Post('logout')
  @HttpCode(HttpStatus.OK)
  async logout(
    @CurrentUser() user: any,
    @Body(ValidationPipe) logoutDto: LogoutDto,
  ) {
    return await this.authService.logout(user.userId, logoutDto);
  }

  @UseGuards(JwtAuthGuard)
  @Patch('profile')
  @HttpCode(HttpStatus.OK)
  async updateProfile(
    @CurrentUser() user: any,
    @Body() updateData: UpdateProfileDto,
  ) {
    return await this.authService.updateProfile(user.userId, updateData);
  }

  // Password Reset Endpoints
  @Public()
  @Post('password/forgot')
  @HttpCode(HttpStatus.OK)
  async forgotPassword(
    @Body(ValidationPipe) dto: RequestPasswordResetDto,
    @Ip() ip: string,
    @Headers('user-agent') userAgent: string,
  ) {
    return await this.passwordResetService.requestPasswordReset(
      dto,
      ip,
      userAgent,
    );
  }

  @Public()
  @Post('password/reset')
  @HttpCode(HttpStatus.OK)
  async resetPassword(
    @Body(ValidationPipe) dto: ResetPasswordDto,
    @Ip() ip: string,
  ) {
    return await this.passwordResetService.resetPassword(dto, ip);
  }

  // Email Verification Endpoints
  @UseGuards(JwtAuthGuard)
  @Post('email/send-verification')
  @HttpCode(HttpStatus.OK)
  async sendEmailVerification(@CurrentUser() user: any) {
    return await this.passwordResetService.sendEmailVerification(user.userId);
  }

  @Public()
  @Post('email/verify')
  @HttpCode(HttpStatus.OK)
  async verifyEmail(@Body(ValidationPipe) dto: VerifyEmailDto) {
    return await this.passwordResetService.verifyEmail(dto);
  }

  @UseGuards(JwtAuthGuard)
  @Post('email/resend-verification')
  @HttpCode(HttpStatus.OK)
  async resendEmailVerification(@CurrentUser() user: any) {
    return await this.passwordResetService.resendEmailVerification(user.userId);
  }

  // MFA Endpoints
  @UseGuards(JwtAuthGuard)
  @Get('mfa/status')
  @HttpCode(HttpStatus.OK)
  async getMFAStatus(@CurrentUser() user: any) {
    return await this.mfaService.getMFAStatus(user.userId);
  }

  @UseGuards(JwtAuthGuard)
  @Post('mfa/totp/setup')
  @HttpCode(HttpStatus.OK)
  async setupTOTP(@CurrentUser() user: any) {
    return await this.mfaService.setupTOTP(user.userId);
  }

  @UseGuards(JwtAuthGuard)
  @Post('mfa/totp/enable')
  @HttpCode(HttpStatus.OK)
  async enableTOTP(
    @CurrentUser() user: any,
    @Body(ValidationPipe) dto: EnableTOTPDto,
  ) {
    return await this.mfaService.verifyAndEnableTOTP(user.userId, dto.code);
  }

  @UseGuards(JwtAuthGuard)
  @Post('mfa/email/setup')
  @HttpCode(HttpStatus.OK)
  async setupEmailMFA(@CurrentUser() user: any) {
    return await this.mfaService.setupEmailMFA(user.userId);
  }

  @Public()
  @Post('mfa/email/send-code')
  @HttpCode(HttpStatus.OK)
  async sendEmailMFACode(@Body() body: { userId: string }) {
    return await this.mfaService.sendEmailMFACode(body.userId);
  }

  @Public()
  @Post('mfa/verify')
  @HttpCode(HttpStatus.OK)
  async verifyMFACode(@Body() body: { userId: string; code: string }) {
    return await this.mfaService.verifyMFACode(body.userId, body.code);
  }

  @UseGuards(JwtAuthGuard)
  @Delete('mfa')
  @HttpCode(HttpStatus.OK)
  async disableMFA(@CurrentUser() user: any) {
    return await this.mfaService.disableMFA(user.userId);
  }

  @UseGuards(JwtAuthGuard)
  @Post('mfa/backup-codes/regenerate')
  @HttpCode(HttpStatus.OK)
  async regenerateBackupCodes(@CurrentUser() user: any) {
    const codes = await this.mfaService.regenerateBackupCodes(user.userId);
    return {
      success: true,
      backupCodes: codes,
    };
  }

  // Session Management Endpoints
  @UseGuards(JwtAuthGuard)
  @Get('sessions')
  @HttpCode(HttpStatus.OK)
  async getSessions(@CurrentUser() user: any, @Req() request: any) {
    // Extract current token ID from request if available
    const currentTokenId = request.user?.tokenId;
    const sessions = await this.sessionService.getUserSessions(
      user.userId,
      currentTokenId,
    );
    return {
      success: true,
      sessions,
      totalCount: sessions.length,
    };
  }

  @UseGuards(JwtAuthGuard)
  @Delete('sessions/:sessionId')
  @HttpCode(HttpStatus.OK)
  async revokeSession(
    @CurrentUser() user: any,
    @Param('sessionId') sessionId: string,
    @Req() request: any,
  ) {
    const currentTokenId = request.user?.tokenId;
    return await this.sessionService.revokeSession(
      user.userId,
      sessionId,
      currentTokenId,
    );
  }

  @UseGuards(JwtAuthGuard)
  @Delete('sessions')
  @HttpCode(HttpStatus.OK)
  async revokeAllSessions(@CurrentUser() user: any, @Req() request: any) {
    const currentTokenId = request.user?.tokenId;
    return await this.sessionService.revokeAllSessions(
      user.userId,
      currentTokenId,
    );
  }

  @UseGuards(JwtAuthGuard)
  @Get('sessions/count')
  @HttpCode(HttpStatus.OK)
  async getSessionCount(@CurrentUser() user: any) {
    const count = await this.sessionService.getActiveSessionCount(user.userId);
    return {
      success: true,
      activeSessionCount: count,
    };
  }

  // Device Trust Management Endpoints
  @UseGuards(JwtAuthGuard)
  @Get('devices')
  @HttpCode(HttpStatus.OK)
  async getDevices(@CurrentUser() user: any, @Req() request: any) {
    const currentDeviceId = request.user?.deviceId;
    const devices = await this.deviceTrustService.getUserDevices(
      user.userId,
      currentDeviceId,
    );
    return {
      success: true,
      devices,
      totalCount: devices.length,
    };
  }

  @UseGuards(JwtAuthGuard)
  @Get('devices/trusted')
  @HttpCode(HttpStatus.OK)
  async getTrustedDevices(@CurrentUser() user: any) {
    const devices = await this.deviceTrustService.getTrustedDevices(
      user.userId,
    );
    return {
      success: true,
      devices,
      totalCount: devices.length,
    };
  }

  @UseGuards(JwtAuthGuard)
  @Get('devices/count')
  @HttpCode(HttpStatus.OK)
  async getDeviceCount(@CurrentUser() user: any) {
    const counts = await this.deviceTrustService.getDeviceCount(user.userId);
    return {
      success: true,
      ...counts,
    };
  }

  @UseGuards(JwtAuthGuard)
  @Post('devices/:deviceId/trust')
  @HttpCode(HttpStatus.OK)
  async trustDevice(
    @CurrentUser() user: any,
    @Param('deviceId') deviceId: string,
  ) {
    return await this.deviceTrustService.trustDevice(user.userId, deviceId);
  }

  @UseGuards(JwtAuthGuard)
  @Delete('devices/:deviceId/trust')
  @HttpCode(HttpStatus.OK)
  async revokeDeviceTrust(
    @CurrentUser() user: any,
    @Param('deviceId') deviceId: string,
    @Req() request: any,
  ) {
    const currentDeviceId = request.user?.deviceId;
    return await this.deviceTrustService.revokeDeviceTrust(
      user.userId,
      deviceId,
      currentDeviceId,
    );
  }

  @UseGuards(JwtAuthGuard)
  @Delete('devices/:deviceId')
  @HttpCode(HttpStatus.OK)
  async removeDevice(
    @CurrentUser() user: any,
    @Param('deviceId') deviceId: string,
    @Req() request: any,
  ) {
    const currentDeviceId = request.user?.deviceId;
    return await this.deviceTrustService.removeDevice(
      user.userId,
      deviceId,
      currentDeviceId,
    );
  }

  @UseGuards(JwtAuthGuard)
  @Delete('devices')
  @HttpCode(HttpStatus.OK)
  async removeAllDevices(@CurrentUser() user: any, @Req() request: any) {
    const currentDeviceId = request.user?.deviceId;
    return await this.deviceTrustService.removeAllDevices(
      user.userId,
      currentDeviceId,
    );
  }

  // ==================== Biometric Authentication Endpoints ====================

  @UseGuards(JwtAuthGuard)
  @Post('biometric/register/options')
  @HttpCode(HttpStatus.OK)
  async getBiometricRegistrationOptions(
    @CurrentUser() user: any,
    @Body() body: { friendlyName?: string },
  ) {
    return await this.biometricAuthService.generateRegistrationOptions({
      userId: user.userId,
      friendlyName: body.friendlyName,
    });
  }

  @UseGuards(JwtAuthGuard)
  @Post('biometric/register/verify')
  @HttpCode(HttpStatus.OK)
  async verifyBiometricRegistration(
    @CurrentUser() user: any,
    @Body() body: { response: any; friendlyName?: string },
  ) {
    return await this.biometricAuthService.verifyRegistrationResponse(
      user.userId,
      body.response,
      body.friendlyName,
    );
  }

  @Public()
  @Post('biometric/authenticate/options')
  @HttpCode(HttpStatus.OK)
  async getBiometricAuthenticationOptions(@Body() body: { email?: string }) {
    return await this.biometricAuthService.generateAuthenticationOptions({
      email: body.email,
    });
  }

  @Public()
  @Post('biometric/authenticate/verify')
  @HttpCode(HttpStatus.OK)
  async verifyBiometricAuthentication(
    @Body()
    body: {
      response: any;
      email?: string;
      deviceInfo?: { deviceId: string; platform: string };
    },
    @Ip() ipAddress: string,
    @Headers('user-agent') userAgent: string,
  ) {
    const deviceInfo = body.deviceInfo || {
      deviceId: body.response.id.substring(0, 10), // Use credential ID as device ID
      platform: userAgent,
    };

    return await this.biometricAuthService.authenticateWithBiometric(
      body.response,
      deviceInfo,
      body.email,
    );
  }

  @UseGuards(JwtAuthGuard)
  @Get('biometric/credentials')
  @HttpCode(HttpStatus.OK)
  async getBiometricCredentials(@CurrentUser() user: any) {
    const credentials = await this.biometricAuthService.getUserCredentials(
      user.userId,
    );
    return {
      success: true,
      credentials: credentials.map((cred) => ({
        id: cred.id,
        friendlyName: cred.friendlyName,
        deviceType: cred.deviceType,
        createdAt: cred.createdAt,
        lastUsed: cred.lastUsed,
        backedUp: cred.backedUp,
      })),
    };
  }

  @UseGuards(JwtAuthGuard)
  @Delete('biometric/credentials/:credentialId')
  @HttpCode(HttpStatus.OK)
  async deleteBiometricCredential(
    @CurrentUser() user: any,
    @Param('credentialId') credentialId: string,
  ) {
    return await this.biometricAuthService.deleteCredential(
      user.userId,
      credentialId,
    );
  }

  @UseGuards(JwtAuthGuard)
  @Patch('biometric/credentials/:credentialId')
  @HttpCode(HttpStatus.OK)
  async updateBiometricCredentialName(
    @CurrentUser() user: any,
    @Param('credentialId') credentialId: string,
    @Body() body: { friendlyName: string },
  ) {
    return await this.biometricAuthService.updateCredentialName(
      user.userId,
      credentialId,
      body.friendlyName,
    );
  }

  @UseGuards(JwtAuthGuard)
  @Get('biometric/has-credentials')
  @HttpCode(HttpStatus.OK)
  async hasBiometricCredentials(@CurrentUser() user: any) {
    const hasCredentials = await this.biometricAuthService.hasCredentials(
      user.userId,
    );
    return {
      success: true,
      hasCredentials,
    };
  }

  // EULA Acceptance Endpoint
  @UseGuards(JwtAuthGuard)
  @Post('eula/accept')
  @HttpCode(HttpStatus.OK)
  async acceptEULA(
    @CurrentUser() user: any,
    @Body() body: { version?: string },
  ) {
    return await this.authService.acceptEULA(
      user.userId,
      body.version || '2025-10-04',
    );
  }
}
