import {
  Body,
  Controller,
  Get,
  Post,
  Req,
  Res,
  UnauthorizedException,
} from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import type { Request, Response } from 'express';
import { changePasswordRequestSchema, loginRequestSchema } from '@ali-ismail/contracts';
import { AuthService } from './auth.service';
import { Public } from '../common/public.decorator';
import { CSRF_COOKIE, computeCsrfToken } from '../common/csrf';
import { PrismaService } from '../prisma/prisma.service';
import type { AuthUserContext } from '../common/request-context';

const COOKIE_NAME = 'tms_session';

type ReqAuth = Request & {
  authUser?: AuthUserContext;
  correlationId?: string;
  cookies?: Record<string, string | undefined>;
};

@ApiTags('auth')
@Controller('auth')
export class AuthController {
  constructor(
    private readonly auth: AuthService,
    private readonly prisma: PrismaService,
  ) {}

  private cookieOptions(expiresAt: Date) {
    // Fail-safe default: secure cookies ON in production unless explicitly
    // disabled; explicit COOKIE_SECURE always wins in any environment.
    const secure =
      process.env.COOKIE_SECURE != null
        ? process.env.COOKIE_SECURE === 'true'
        : process.env.NODE_ENV === 'production';
    return {
      httpOnly: true,
      sameSite: 'lax' as const,
      secure,
      path: '/',
      expires: expiresAt,
    };
  }

  private meta(req: ReqAuth) {
    return {
      ip: req.ip ?? undefined,
      userAgent: typeof req.headers['user-agent'] === 'string' ? req.headers['user-agent'] : undefined,
      correlationId: req.correlationId ?? undefined,
    };
  }

  @Public()
  @Post('login')
  async login(
    @Body() body: unknown,
    @Req() req: ReqAuth,
    @Res({ passthrough: true }) res: Response,
  ) {
    const dto = loginRequestSchema.parse(body);
    const result = await this.auth.login(dto, this.meta(req));
    res.cookie(COOKIE_NAME, result.rawToken, this.cookieOptions(result.expiresAt));
    // CSRF token: non-HttpOnly so same-origin JS can echo it in x-csrf-token.
    res.cookie(CSRF_COOKIE, computeCsrfToken(result.rawToken), {
      ...this.cookieOptions(result.expiresAt),
      httpOnly: false,
    });
    return { user: result.user };
  }

  @Post('logout')
  async logout(@Req() req: ReqAuth, @Res({ passthrough: true }) res: Response) {
    const token = req.cookies?.[COOKIE_NAME];
    await this.auth.logout(token, this.meta(req));
    res.clearCookie(COOKIE_NAME, { path: '/' });
    res.clearCookie(CSRF_COOKIE, { path: '/' });
    return { ok: true };
  }

  @Post('logout-all')
  async logoutAll(@Req() req: ReqAuth, @Res({ passthrough: true }) res: Response) {
    const user = req.authUser;
    if (!user) throw new UnauthorizedException();
    await this.auth.logoutAll(user.id, user.organizationId);
    res.clearCookie(COOKIE_NAME, { path: '/' });
    res.clearCookie(CSRF_COOKIE, { path: '/' });
    return { ok: true };
  }

  @Post('change-password')
  async changePassword(
    @Body() body: unknown,
    @Req() req: ReqAuth,
    @Res({ passthrough: true }) res: Response,
  ) {
    if (!req.authUser) throw new UnauthorizedException();
    await this.auth.changePassword(
      req.authUser.id,
      req.authUser.organizationId,
      changePasswordRequestSchema.parse(body),
      this.meta(req),
    );
    res.clearCookie(COOKIE_NAME, { path: '/' });
    res.clearCookie(CSRF_COOKIE, { path: '/' });
    return { ok: true };
  }

  @Get('me')
  async me(@Req() req: ReqAuth) {
    if (!req.authUser) throw new UnauthorizedException();
    const org = await this.prisma.organization.findUniqueOrThrow({
      where: { id: req.authUser.organizationId },
      include: { settings: true },
    });
    return {
      user: req.authUser,
      organization: {
        id: org.id,
        code: org.code,
        name: org.name,
        businessName: org.settings?.businessName,
        timezone: org.settings?.timezone,
        currencyCode: org.settings?.currencyCode,
      },
    };
  }
}