import {
  Body,
  Controller,
  Get,
  HttpCode,
  HttpStatus,
  Post,
  Req,
  UnauthorizedException,
  UseGuards,
} from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import { Request } from 'express';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
import {
  ChangePasswordDto,
  ForgotPasswordDto,
  RefreshTokenDto,
  ResetPasswordDto,
} from './dto/auth.dto';
import { JwtAuthGuard } from './guards/jwt-auth.guard';
import { CurrentUser, AuthenticatedUser } from '../common/decorators/current-user.decorator';
import { AuditLogsService } from '../audit-logs/audit-logs.service';

@Controller('api/v1/admin/auth')
export class AuthController {
  constructor(
    private readonly authService: AuthService,
    private readonly auditService: AuditLogsService,
  ) {}

  @Post('login')
  @HttpCode(HttpStatus.OK)
  @Throttle({ default: { limit: 5, ttl: 60_000 } })
  async login(@Body() dto: LoginDto, @Req() req: Request) {
    const user = await this.authService.validateUser(dto.email, dto.password);
    if (!user) {
      await this.auditService.record({
        action: 'LOGIN_FAILED',
        entityType: 'user',
        newValue: { email: dto.email },
        ip: this.ip(req),
      });
      throw new UnauthorizedException('Invalid credentials');
    }

    const result = await this.authService.login(user, this.context(req));

    await this.auditService.record({
      userId: user.id,
      action: 'LOGIN_SUCCEEDED',
      entityType: 'user',
      entityId: Number(user.id),
      ip: this.ip(req),
    });

    return { success: true, message: 'Success', data: result };
  }

  @Post('refresh')
  @HttpCode(HttpStatus.OK)
  @Throttle({ default: { limit: 20, ttl: 60_000 } })
  async refresh(@Body() dto: RefreshTokenDto, @Req() req: Request) {
    const tokens = await this.authService.refresh(dto.refreshToken, this.context(req));
    return { success: true, message: 'Success', data: tokens };
  }

  @Post('logout')
  @HttpCode(HttpStatus.OK)
  @UseGuards(JwtAuthGuard)
  async logout(@Body() dto: Partial<RefreshTokenDto>, @CurrentUser() user: AuthenticatedUser) {
    await this.authService.logout(dto?.refreshToken, user.id);
    return { success: true, message: 'Logged out' };
  }

  @Get('me')
  @UseGuards(JwtAuthGuard)
  async me(@CurrentUser() user: AuthenticatedUser) {
    const data = await this.authService.profile(user.id);
    return { success: true, message: 'Success', data };
  }

  @Post('forgot-password')
  @HttpCode(HttpStatus.OK)
  @Throttle({ default: { limit: 3, ttl: 60_000 } })
  async forgotPassword(@Body() dto: ForgotPasswordDto) {
    const token = await this.authService.createPasswordReset(dto.email);

    // The response never reveals whether the account exists. Outside
    // production the token is returned so the flow is testable without SMTP.
    const exposeToken = process.env.NODE_ENV !== 'production' && token;

    return {
      success: true,
      message: 'If the account exists, a reset link has been sent',
      ...(exposeToken ? { data: { resetToken: token } } : {}),
    };
  }

  @Post('reset-password')
  @HttpCode(HttpStatus.OK)
  @Throttle({ default: { limit: 5, ttl: 60_000 } })
  async resetPassword(@Body() dto: ResetPasswordDto) {
    await this.authService.resetPassword(dto.token, dto.newPassword);
    return { success: true, message: 'Password has been reset' };
  }

  @Post('change-password')
  @HttpCode(HttpStatus.OK)
  @UseGuards(JwtAuthGuard)
  async changePassword(
    @Body() dto: ChangePasswordDto,
    @CurrentUser() user: AuthenticatedUser,
  ) {
    await this.authService.changePassword(user.id, dto.currentPassword, dto.newPassword);
    return { success: true, message: 'Password changed' };
  }

  private ip(req: Request): string | undefined {
    const forwarded = req.headers['x-forwarded-for'];
    if (typeof forwarded === 'string' && forwarded) return forwarded.split(',')[0].trim();
    return req.ip;
  }

  private context(req: Request) {
    return { ip: this.ip(req), userAgent: req.headers['user-agent'] };
  }
}
