import { Type } from 'class-transformer';
import {
  IsEnum,
  IsInt,
  IsISO8601,
  IsOptional,
  IsString,
  Max,
  MaxLength,
  Min,
} from 'class-validator';

/**
 * Query parameters accepted by `GET /api/admin/audit`. Bound from
 * the URL via Nest's `@Query()` + the global `ValidationPipe`
 * (whitelist + forbidNonWhitelisted + transform). Unknown keys are
 * rejected at the pipe — saves the controller from having to vet
 * them and shrinks the attack surface for query-string smuggling.
 *
 * Pagination is offset/limit, kept simple in v1:
 *   - `limit` defaults to 50 and is capped at {@link MAX_LIMIT} (200)
 *     so a single request can't pull tens of thousands of rows in
 *     one trip and stall the BFF's event loop on JSON serialisation.
 *   - `offset` defaults to 0. For deeper pagination the SPA should
 *     refine the date-range filter rather than walking thousands of
 *     offsets — Postgres' offset pagination performance degrades
 *     past a few thousand rows.
 *
 * Filters all OR-compose nothing — each is an additional AND clause.
 * They are deliberately exact-match except `subjectPrefix` (which
 * uses `LIKE 'prefix%'`) because the existing event catalogue uses
 * structured prefixes like `session:<sid>` or `route:GET /api/...`.
 */
export const MAX_LIMIT = 200;
export const DEFAULT_LIMIT = 50;

/** Subset of `AuditAudience` accepted by the filter. */
const AUDIT_AUDIENCES = ['workforce', 'customer'] as const;
type AuditAudienceFilter = (typeof AUDIT_AUDIENCES)[number];

/** Subset of `AuditOutcome` accepted by the filter. */
const AUDIT_OUTCOMES = ['success', 'failure', 'denied'] as const;
type AuditOutcomeFilter = (typeof AUDIT_OUTCOMES)[number];

export class AdminAuditQueryDto {
  @IsOptional()
  @IsString()
  @MaxLength(128)
  eventType?: string;

  @IsOptional()
  @IsString()
  @MaxLength(128)
  actorIdHash?: string;

  @IsOptional()
  @IsEnum(AUDIT_AUDIENCES)
  audience?: AuditAudienceFilter;

  @IsOptional()
  @IsEnum(AUDIT_OUTCOMES)
  outcome?: AuditOutcomeFilter;

  /**
   * Prefix match on the `subject` column. Matches the
   * structured-prefix convention used by the event catalogue —
   * e.g. `session:` to find every event tied to a particular
   * session, or `GET /api/admin/` to find every denied admin
   * access attempt.
   */
  @IsOptional()
  @IsString()
  @MaxLength(128)
  subjectPrefix?: string;

  /** ISO-8601 lower bound on `created_at` (inclusive). */
  @IsOptional()
  @IsISO8601()
  createdAtFrom?: string;

  /** ISO-8601 upper bound on `created_at` (exclusive). */
  @IsOptional()
  @IsISO8601()
  createdAtTo?: string;

  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(1)
  @Max(MAX_LIMIT)
  limit?: number;

  @IsOptional()
  @Type(() => Number)
  @IsInt()
  @Min(0)
  offset?: number;
}
