import { randomUUID } from "node:crypto";
import { getPool } from "../db/pool";

export interface AuditLogEntity {
  id: string;
  actor_type: "admin" | "merchant" | "device" | "webhook" | "system";
  actor_id?: string;
  action: string;
  entity_type: string;
  entity_id: string;
  before_json?: Record<string, unknown> | null;
  after_json?: Record<string, unknown> | null;
  source_ip?: string;
  request_id?: string;
  trace_id?: string;
  created_at: string;
}

function nowIso() {
  return new Date().toISOString();
}

function mapAuditLog(row: any): AuditLogEntity {
  return {
    id: row.id,
    actor_type: row.actor_type,
    actor_id: row.actor_id || undefined,
    action: row.action,
    entity_type: row.entity_type,
    entity_id: row.entity_id,
    before_json: row.before_json || null,
    after_json: row.after_json || null,
    source_ip: row.source_ip || undefined,
    request_id: row.request_id || undefined,
    trace_id: row.trace_id || undefined,
    created_at: row.created_at
  };
}

export async function createAuditLog(payload: {
  actor_type: AuditLogEntity["actor_type"];
  actor_id?: string;
  action: string;
  entity_type: string;
  entity_id: string;
  before_json?: unknown;
  after_json?: unknown;
  source_ip?: string;
  request_id?: string;
  trace_id?: string;
}): Promise<AuditLogEntity> {
  const { rows } = await getPool().query(
    `INSERT INTO audit_logs (
      id,
      actor_type,
      actor_id,
      action,
      entity_type,
      entity_id,
      before_json,
      after_json,
      source_ip,
      request_id,
      trace_id,
      created_at
    ) VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12)
    RETURNING *`,
    [
      randomUUID(),
      payload.actor_type,
      payload.actor_id || null,
      payload.action,
      payload.entity_type,
      payload.entity_id,
      payload.before_json || null,
      payload.after_json || null,
      payload.source_ip || null,
      payload.request_id || null,
      payload.trace_id || null,
      nowIso()
    ]
  );

  return mapAuditLog(rows[0]);
}

export async function listAuditLogs(filter?: {
  entity_type?: string;
  entity_id?: string;
  action?: string;
  action_contains?: string;
  from?: string;
  to?: string;
  limit?: number;
}): Promise<AuditLogEntity[]> {
  const clauses: string[] = [];
  const params: unknown[] = [];
  let i = 1;

  if (filter?.entity_type) {
    clauses.push(`entity_type = $${i++}`);
    params.push(filter.entity_type);
  }

  if (filter?.entity_id) {
    clauses.push(`entity_id = $${i++}`);
    params.push(filter.entity_id);
  }

  if (filter?.action) {
    clauses.push(`action = $${i++}`);
    params.push(filter.action);
  }

  if (filter?.action_contains) {
    clauses.push(`action ILIKE $${i++}`);
    params.push(`%${filter.action_contains}%`);
  }

  if (filter?.from) {
    clauses.push(`created_at >= $${i++}`);
    params.push(filter.from);
  }

  if (filter?.to) {
    clauses.push(`created_at <= $${i++}`);
    params.push(filter.to);
  }

  const limit = Math.min(Math.max(filter?.limit || 100, 1), 500);
  const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
  const { rows } = await getPool().query(
    `SELECT * FROM audit_logs ${where} ORDER BY created_at DESC LIMIT ${limit}`,
    params
  );

  return rows.map(mapAuditLog);
}

export function toAuditLogPayload(auditLog: AuditLogEntity) {
  return { ...auditLog };
}
