import { logAudit } from '@platform-modules/audit';
import type { AuditSchema } from '@platform-modules/audit';
import type { Querier } from '@platform-modules/db';

/** Idempotent DDL for audit_log — mirrors src/db/audit-schema.sql. */
export const auditTableSql = (): string =>
  `
CREATE TABLE IF NOT EXISTS audit_log (
  id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
  actor_id text,
  actor_type text,
  actor_label text,
  action text NOT NULL,
  entity_type text NOT NULL,
  entity_id text NOT NULL,
  metadata jsonb,
  ip text,
  tenant_id text,
  created_at timestamptz(3) NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS audit_log_tenant_created_idx ON audit_log (tenant_id, created_at DESC);
CREATE INDEX IF NOT EXISTS audit_log_tenant_entity_idx ON audit_log (tenant_id, entity_type, entity_id);
CREATE INDEX IF NOT EXISTS audit_log_tenant_actor_idx ON audit_log (tenant_id, actor_id);
`.trim();

/** Per-request actor context distilled from `requireAdmin` + the request. */
export type AuditActorCtx = {
  actorId: string;
  ip: string | null;
};

export type AuditEventInput = {
  action: string;
  entityType: string;
  entityId: string;
  metadata?: Record<string, unknown> | null;
};

/**
 * Best-effort audit write for the mod-cms admin routes. `logAudit` NEVER throws and is
 * DECOUPLED — call this AFTER the mutation has committed; a failed audit write must not
 * fail or roll back the mutation. mod-cms is single-tenant → `tenantId` is always null.
 */
export async function recordAudit(
  db: Querier<AuditSchema>,
  ctx: AuditActorCtx,
  event: AuditEventInput,
): Promise<void> {
  const result = await logAudit(db, {
    actorId: ctx.actorId,
    actorType: 'admin',
    actorLabel: null,
    action: event.action,
    entityType: event.entityType,
    entityId: event.entityId,
    metadata: event.metadata ?? null,
    ip: ctx.ip,
    tenantId: null,
  });
  if (!result.logged) {
    // Observability only — the mutation already succeeded; never rethrow.
    console.warn('mod-cms audit write failed', { action: event.action, error: result.error });
  }
}

/** Extract the client IP from the Astro/CF request, or null. */
export function clientIp(request: Request): string | null {
  return request.headers.get('cf-connecting-ip') ?? null;
}