import { Body, Controller, Get, Post, Patch, HttpCode, HttpStatus, Ip } from '@nestjs/common';
import { ApiTags, ApiBearerAuth, ApiOperation } from '@nestjs/swagger';
import { IsEmail, IsEnum, IsOptional, IsString, Length } from 'class-validator';
import { MagicLinkPurpose } from '@prisma/client';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';
import { RefreshTokenDto } from './dto/refresh-token.dto';
import { Public } from '../../common/decorators/public.decorator';
import { CurrentUser, AuthenticatedUser } from '../../common/decorators/current-user.decorator';

class MfaCodeDto {
  @IsString()
  @Length(6, 8)
  code!: string;
}

class UpdateProfileDto {
  @IsOptional() @IsString() firstName?: string;
  @IsOptional() @IsString() lastName?: string;
  @IsOptional() @IsString() phone?: string;
}

class ChangePasswordDto {
  @IsString() currentPassword!: string;
  @IsString() @Length(8, 128) newPassword!: string;
}

class MagicLinkRequestDto {
  @IsEmail()
  email!: string;

  @IsOptional()
  @IsEnum(MagicLinkPurpose)
  purpose?: MagicLinkPurpose;
}

class MagicLinkVerifyDto {
  @IsString()
  @Length(32, 256)
  token!: string;
}

@ApiTags('Auth')
@Controller('auth')
export class AuthController {
  constructor(private readonly authService: AuthService) {}

  @Public()
  @Post('register')
  @ApiOperation({ summary: 'Register a new user' })
  register(@Body() dto: RegisterDto) {
    return this.authService.register(dto);
  }

  @Public()
  @Post('login')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Login with email & password' })
  login(@Body() dto: LoginDto) {
    return this.authService.login(dto);
  }

  @Public()
  @Post('refresh')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Rotate refresh token' })
  refresh(@Body() dto: RefreshTokenDto) {
    return this.authService.refresh(dto);
  }

  @ApiBearerAuth()
  @Post('logout')
  @HttpCode(HttpStatus.OK)
  logout(@CurrentUser() user: AuthenticatedUser, @Body() dto: RefreshTokenDto) {
    return this.authService.logout(user.userId, dto.refreshToken);
  }

  @ApiBearerAuth()
  @Get('me')
  @ApiOperation({ summary: 'Get current user profile + roles' })
  me(@CurrentUser() user: AuthenticatedUser) {
    return this.authService.getProfile(user.userId);
  }

  @ApiBearerAuth()
  @Patch('me')
  @ApiOperation({ summary: 'Update current user profile (name, phone)' })
  updateProfile(@CurrentUser() user: AuthenticatedUser, @Body() dto: UpdateProfileDto) {
    return this.authService.updateProfile(user.userId, dto);
  }

  @ApiBearerAuth()
  @Post('change-password')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Change current user password' })
  changePassword(@CurrentUser() user: AuthenticatedUser, @Body() dto: ChangePasswordDto) {
    return this.authService.changePassword(user.userId, dto.currentPassword, dto.newPassword);
  }

  // -------- MFA (TOTP) --------

  @ApiBearerAuth()
  @Post('mfa/enroll')
  @ApiOperation({ summary: 'Generate a TOTP secret + otpauth URL (not yet active)' })
  mfaEnroll(@CurrentUser() user: AuthenticatedUser) {
    return this.authService.mfaEnroll(user.userId);
  }

  @ApiBearerAuth()
  @Post('mfa/activate')
  @ApiOperation({ summary: 'Verify first TOTP code and enable MFA on the account' })
  mfaActivate(@CurrentUser() user: AuthenticatedUser, @Body() dto: MfaCodeDto) {
    return this.authService.mfaActivate(user.userId, dto.code);
  }

  @ApiBearerAuth()
  @Post('mfa/disable')
  @ApiOperation({ summary: 'Disable MFA (requires current TOTP code)' })
  mfaDisable(@CurrentUser() user: AuthenticatedUser, @Body() dto: MfaCodeDto) {
    return this.authService.mfaDisable(user.userId, dto.code);
  }

  // -------- Magic links (P0-1, P0-10) --------

  @Public()
  @Post('magic-link/request')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Demande un lien magique (LOGIN 30 min ou PRESTATAIRE_ACCESS 30 j)' })
  requestMagicLink(@Body() dto: MagicLinkRequestDto) {
    return this.authService.requestMagicLink(dto.email, dto.purpose);
  }

  @Public()
  @Post('magic-link/verify')
  @HttpCode(HttpStatus.OK)
  @ApiOperation({ summary: 'Échange un token magique contre une session JWT' })
  verifyMagicLink(@Body() dto: MagicLinkVerifyDto, @Ip() ip: string) {
    return this.authService.verifyMagicLink(dto.token, ip);
  }
}
