import {
  Body,
  Controller,
  Get,
  HttpCode,
  Patch,
  Post,
  Req,
  Res,
  UseGuards,
} from "@nestjs/common";
import { Request, Response } from "express";
import { LocalAuthGuard } from "./local-auth.guard";
import { AuthenticatedGuard } from "./authenticated.guard";
import { LoginDto } from "./login.dto";
import { UpdatePreferencesDto } from "./update-preferences.dto";
import { abilitiesFor, Role } from "./abilities";
import { UsersService } from "../users/users.service";

/** Attach the resolved ability map so the web can gate its UI off one payload. */
function withAbilities(user: unknown) {
  const u = user as { role?: Role } | undefined;
  if (!u?.role) return u;
  return { ...u, abilities: abilitiesFor(u.role) };
}

@Controller("auth")
export class AuthController {
  constructor(private readonly users: UsersService) {}

  // LoginDto is only used for request-shape documentation/validation here —
  // the actual credential check happens inside LocalStrategy via Passport,
  // which populates req.user before this handler runs.
  @UseGuards(LocalAuthGuard)
  @Post("login")
  @HttpCode(200)
  login(@Req() req: Request, @Res({ passthrough: true }) _res: Response, _body?: LoginDto) {
    return withAbilities(req.user);
  }

  @UseGuards(AuthenticatedGuard)
  @Get("me")
  me(@Req() req: Request) {
    return withAbilities(req.user);
  }

  /**
   * Update the caller's own UI preferences. Deliberately not on /users/:id —
   * that controller is ADMIN-only, and this has to work for every role. The
   * target is always the session's own user id, never a body parameter.
   */
  @UseGuards(AuthenticatedGuard)
  @Patch("preferences")
  async updatePreferences(@Req() req: Request, @Body() dto: UpdatePreferencesDto) {
    const id = (req.user as { id: string }).id;
    const user = await this.users.updatePreferences(id, dto.uiScale);
    return withAbilities(user);
  }

  @Post("logout")
  @HttpCode(200)
  logout(@Req() req: Request) {
    return new Promise((resolve, reject) => {
      req.logout((err) => {
        if (err) {
          reject(err);
          return;
        }
        resolve({ success: true });
      });
    });
  }
}
