import { Controller, Get, Query, Req } from '@nestjs/common';
import { ApiCookieAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import type { Request } from 'express';
import { AuditWriter } from '../audit/audit.service';
import { AdminAuditQueryDto } from './audit-query.dto';
import { AuditReader, type AdminAuditPage } from './audit-reader.service';
import { RequireAdmin } from './require-admin.decorator';

/**
 * `GET /api/admin/audit` — paginated audit-log viewer per
 * ADR-0020's v1 admin module catalogue. Reads `audit.events` via
 * the `audit_reader` Postgres role (locked inside the `AuditReader`
 * service), shapes the result for SPA consumption, and emits an
 * `admin.audit.query` row so the read itself is auditable per
 * ADR-0020 §"Read actions are also captured … to deter fishing
 * expeditions".
 *
 * The query DTO (`AdminAuditQueryDto`) carries every supported
 * filter + pagination knob; Nest's global ValidationPipe rejects
 * unknown keys before they reach this handler.
 *
 * `@RequireAdmin()` at the class level gates the route on the
 * `admin` Entra app role per ADR-0020 §"Auth — same Entra ID".
 * `@RequireMfa()` is not applied here in v1: the entire admin
 * surface already sits behind a freshly-MFA'd session at the
 * sign-in entry, and the audit row itself is the per-query
 * deterrent. A future security review can layer `@RequireMfa`
 * with a tighter freshness here without touching this code's
 * shape — that's exactly why the decorator was designed-in.
 */
@ApiTags('admin (audit log)')
@ApiCookieAuth('portal_admin_session')
@Controller('admin/audit')
@RequireAdmin()
export class AdminAuditController {
  constructor(
    private readonly auditReader: AuditReader,
    private readonly audit: AuditWriter,
  ) {}

  @ApiOperation({
    summary: 'Paginated audit-log query — emits `admin.audit.query` on every call',
  })
  @Get()
  async list(@Req() req: Request, @Query() filters: AdminAuditQueryDto): Promise<AdminAuditPage> {
    const page = await this.auditReader.findEvents(filters);

    // Guard guarantees `req.session.user`; we still defensively
    // narrow rather than assert — the audit emission is what
    // makes this read auditable, so if we somehow get here without
    // an actor we should still surface the read (with no actor
    // hash) rather than silently swallow it.
    const actorOid = req.session.user?.oid;
    if (actorOid !== undefined) {
      await this.audit.adminAuditQuery({
        actor: { oid: actorOid },
        filters: { ...filters },
        resultCount: page.items.length,
      });
    }

    return page;
  }
}
