import { Body, Controller, Get, Post, Req, Res, HttpCode } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { Request, Response } from 'express';
import { LoginSchema, PlatformLoginSchema, RefreshSchema, ChangePasswordSchema, LoginDto, PlatformLoginDto } from '@wms/shared';
import { z } from 'zod';
import { Public, Ctx } from '@/common/auth/decorators';
import { zodBody } from '@/common/validation/zod.pipe';
import { RequestContext } from '@/common/context/request-context';
import { loadEnv } from '@/config/env';
import { AuthService } from './auth.service';

const REFRESH_COOKIE = 'wms_refresh';

/**
 * Web clients receive the refresh token as an httpOnly cookie; handheld / API
 * clients that cannot use cookies receive it in the body and send it back in
 * the refresh request body.
 */
@ApiTags('auth')
@Throttle({ default: { ttl: 60_000, limit: 30 } })
@Controller('auth')
export class AuthController {
  private readonly env = loadEnv();
  constructor(private readonly auth: AuthService) {}

  @Public()
  @Post('login')
  @HttpCode(200)
  async login(@Body(zodBody(LoginSchema)) dto: LoginDto, @Res({ passthrough: true }) res: Response) {
    const result = await this.auth.login(dto);
    this.setRefreshCookie(res, result.tokens.refreshToken);
    return result;
  }

  @Public()
  @Post('platform/login')
  @HttpCode(200)
  platformLogin(@Body(zodBody(PlatformLoginSchema)) dto: PlatformLoginDto) {
    return this.auth.platformLogin(dto);
  }

  @Public()
  @Post('refresh')
  @HttpCode(200)
  async refresh(@Body(zodBody(RefreshSchema)) body: z.infer<typeof RefreshSchema>, @Req() req: Request, @Res({ passthrough: true }) res: Response) {
    const raw = body.refreshToken ?? (req.cookies?.[REFRESH_COOKIE] as string | undefined);
    const result = await this.auth.refresh(raw ?? '');
    this.setRefreshCookie(res, result.tokens.refreshToken);
    return result;
  }

  @Post('logout')
  @HttpCode(204)
  async logout(@Body(zodBody(RefreshSchema)) body: z.infer<typeof RefreshSchema>, @Req() req: Request, @Res({ passthrough: true }) res: Response, @Ctx() ctx: RequestContext) {
    const raw = body.refreshToken ?? (req.cookies?.[REFRESH_COOKIE] as string | undefined);
    await this.auth.logout(raw, ctx.userId, ctx.sessionId);
    res.clearCookie(REFRESH_COOKIE, { path: `/${this.env.API_PREFIX}/auth` });
  }

  @ApiBearerAuth()
  @Get('me')
  me(@Ctx() ctx: RequestContext) {
    if (ctx.actorType === 'PLATFORM_ADMIN') return { id: ctx.actorId, name: ctx.actorName, platform: true, permissions: ['*'] };
    return this.auth.me(ctx.userId!);
  }

  @ApiBearerAuth()
  @Post('change-password')
  @HttpCode(204)
  async changePassword(@Body(zodBody(ChangePasswordSchema)) body: z.infer<typeof ChangePasswordSchema>, @Ctx() ctx: RequestContext) {
    await this.auth.changePassword(ctx.userId!, body.currentPassword, body.newPassword);
  }

  private setRefreshCookie(res: Response, token: string) {
    res.cookie(REFRESH_COOKIE, token, {
      httpOnly: true,
      secure: this.env.COOKIE_SECURE,
      sameSite: 'lax',
      path: `/${this.env.API_PREFIX}/auth`,
      maxAge: this.env.JWT_REFRESH_TTL_DAYS * 86400000,
    });
  }
}
