import { Body, Controller, Get, HttpCode, Post, Req, Res } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import type { FastifyReply, FastifyRequest } from 'fastify';
import { CurrentUser } from '../../common/auth/current-user.decorator';
import type { AuthenticatedUser } from '../../common/auth/authenticated-user';
import { Public } from '../../common/auth/public.decorator';
import { AuthSessionsService } from './auth-sessions.service';
import { AuthService } from './auth.service';
import { LoginDto } from './dto/login.dto';
import { RegisterDto } from './dto/register.dto';

@Controller('auth')
export class AuthController {
  constructor(
    private readonly auth: AuthService,
    private readonly sessions: AuthSessionsService,
    private readonly config: ConfigService,
  ) {}

  @Public()
  @Post('register')
  async register(
    @Body() input: RegisterDto,
    @Req() request: FastifyRequest,
    @Res({ passthrough: true }) reply: FastifyReply,
  ) {
    const result = await this.auth.register(input, this.getSessionContext(request));
    this.setSessionCookie(reply, result.session.token, result.session.expiresAt);
    return { user: result.user };
  }

  @Public()
  @HttpCode(200)
  @Post('login')
  async login(
    @Body() input: LoginDto,
    @Req() request: FastifyRequest,
    @Res({ passthrough: true }) reply: FastifyReply,
  ) {
    const result = await this.auth.login(input, this.getSessionContext(request));
    this.setSessionCookie(reply, result.session.token, result.session.expiresAt);
    return { user: result.user };
  }

  @HttpCode(204)
  @Post('logout')
  async logout(
    @CurrentUser() user: AuthenticatedUser,
    @Res({ passthrough: true }) reply: FastifyReply,
  ): Promise<void> {
    await this.sessions.revoke(user.sessionId);
    reply.clearCookie(this.cookieName, {
      path: '/',
      httpOnly: true,
      secure: this.isProduction,
      sameSite: 'strict',
    });
  }

  @Get('me')
  me(@CurrentUser() user: AuthenticatedUser) {
    return {
      user: {
        id: user.id,
        email: user.email,
        displayName: user.displayName,
        role: user.role,
      },
    };
  }

  private setSessionCookie(reply: FastifyReply, token: string, expiresAt: Date): void {
    reply.setCookie(this.cookieName, token, {
      path: '/',
      httpOnly: true,
      secure: this.isProduction,
      sameSite: 'strict',
      expires: expiresAt,
    });
  }

  private getSessionContext(request: FastifyRequest) {
    return {
      ipAddress: request.ip,
      userAgent: request.headers['user-agent'],
    };
  }

  private get cookieName(): string {
    return this.config.get<string>('SESSION_COOKIE_NAME', 'ck_session');
  }

  private get isProduction(): boolean {
    return this.config.get<string>('NODE_ENV') === 'production';
  }
}
