import { Controller, Get, Req, UnauthorizedException } from '@nestjs/common';
import { ApiCookieAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import type { Request } from 'express';

/**
 * Curated capabilities derived from the active user-portal session,
 * keyed by the binary access decisions the SPA needs to drive its
 * own UI. Deliberately *not* a passthrough of `session.user.roles`:
 * ADR-0009 §"curated public view" keeps the raw `roles` claim
 * server-side; the SPA renders gates and cross-app links from the
 * derived booleans this view exposes.
 *
 * Add a key when the SPA needs a new conditional rendering signal.
 * Never add a key that hands back information not already deducible
 * from the BFF's own server-side authorization decisions — the
 * authoritative gate stays on the BFF guards (`@RequireAdmin`,
 * `@RequireMfa`, …).
 */
export interface CapabilitiesView {
  /**
   * True when the session's roles include the `Portal.Admin` Entra
   * app role — the SPA uses this to surface the "Open Portal Admin"
   * entry in the user menu. The actual `/api/admin/*` access is
   * gated server-side by `AdminRoleGuard`; this flag is UX only.
   */
  readonly canAccessAdmin: boolean;
}

const ADMIN_ROLE = 'Portal.Admin';

/**
 * `GET /api/me/capabilities` — projects the active session's roles
 * onto a curated capabilities view consumed by `portal-shell`. The
 * SPA calls this once per app boot (and on session refresh) to
 * decide what to render: today, whether to expose the "Open Portal
 * Admin" entry in the user menu; future keys land alongside as more
 * conditional UI is required.
 *
 * Per ADR-0009 §"curated public view", the user-portal `/me` does
 * NOT expose the raw `roles` claim — it stays server-side. This
 * endpoint is the documented release-valve for binary UX hints,
 * shaped so consumers can never reconstruct the raw role names.
 *
 * Mounted under `/api/me/*` rather than `/api/auth/*`: auth is the
 * lifecycle (login, callback, /me identity, logout) — capabilities
 * is a derived session view. Keeping the namespaces distinct gives
 * the latter room to grow without bloating the auth surface.
 *
 * The path-routed session middleware in `main.ts` resolves this
 * to the user-portal session (`portal_session`), so admin-only
 * surfaces continue to be reached via `/api/admin/auth/me` for
 * their identity payload and through the corresponding admin-side
 * guards for authorization.
 */
@ApiTags('me')
@ApiCookieAuth('portal_session')
@Controller('me')
export class MeController {
  @ApiOperation({
    summary: 'Curated capabilities derived from the active user-portal session',
  })
  @Get('capabilities')
  capabilities(@Req() req: Request): CapabilitiesView {
    const user = req.session.user;
    if (!user) {
      // Same posture as `/api/auth/me`: 401 (rather than 200 with
      // every-flag-false) so the SPA can distinguish "no session"
      // from "session exists but no flags". Keeps the consumer code
      // path identical between the two reads.
      throw new UnauthorizedException({
        code: 'unauthenticated',
        message: 'Unauthenticated',
      });
    }
    return {
      canAccessAdmin: user.roles.includes(ADMIN_ROLE),
    };
  }
}
