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

export type ExportJobEntity = {
  id: string;
  job_type: string;
  status: "pending" | "running" | "completed" | "failed";
  requested_by?: string;
  request_json: Record<string, unknown>;
  result_content_type?: string;
  result_filename?: string;
  result_body?: string;
  result_storage_path?: string;
  result_size_bytes?: number;
  expires_at?: string;
  error_message?: string;
  created_at: string;
  started_at?: string;
  completed_at?: string;
};

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

function mapExportJob(row: any): ExportJobEntity {
  return {
    id: row.id,
    job_type: row.job_type,
    status: row.status,
    requested_by: row.requested_by || undefined,
    request_json: row.request_json || {},
    result_content_type: row.result_content_type || undefined,
    result_filename: row.result_filename || undefined,
    result_body: row.result_body || undefined,
    result_storage_path: row.result_storage_path || undefined,
    result_size_bytes: row.result_size_bytes === null || row.result_size_bytes === undefined ? undefined : Number(row.result_size_bytes),
    expires_at: row.expires_at || undefined,
    error_message: row.error_message || undefined,
    created_at: row.created_at,
    started_at: row.started_at || undefined,
    completed_at: row.completed_at || undefined
  };
}

export async function createExportJob(payload: {
  job_type: string;
  requested_by?: string;
  request_json?: Record<string, unknown>;
}): Promise<ExportJobEntity> {
  const { rows } = await getPool().query(
    `INSERT INTO export_jobs (id, job_type, status, requested_by, request_json, created_at)
     VALUES ($1,$2,'pending',$3,$4,$5)
     RETURNING *`,
    [randomUUID(), payload.job_type, payload.requested_by || null, payload.request_json || {}, nowIso()]
  );
  return mapExportJob(rows[0]);
}

export async function getExportJobById(id: string): Promise<ExportJobEntity | null> {
  const { rows } = await getPool().query("SELECT * FROM export_jobs WHERE id = $1", [id]);
  return rows[0] ? mapExportJob(rows[0]) : null;
}

export async function listExportJobs(filter?: {
  job_type?: string;
  status?: ExportJobEntity["status"];
  limit?: number;
}): Promise<ExportJobEntity[]> {
  const clauses: string[] = [];
  const params: unknown[] = [];
  let i = 1;
  if (filter?.job_type) {
    clauses.push(`job_type = $${i++}`);
    params.push(filter.job_type);
  }
  if (filter?.status) {
    clauses.push(`status = $${i++}`);
    params.push(filter.status);
  }
  const limit = Math.min(Math.max(filter?.limit || 20, 1), 100);
  params.push(limit);
  const where = clauses.length ? `WHERE ${clauses.join(" AND ")}` : "";
  const { rows } = await getPool().query(
    `SELECT * FROM export_jobs ${where} ORDER BY created_at DESC LIMIT $${i}`,
    params
  );
  return rows.map(mapExportJob);
}

export async function markExportJobRunning(id: string): Promise<ExportJobEntity> {
  const { rows } = await getPool().query(
    `UPDATE export_jobs
        SET status = 'running',
            started_at = COALESCE(started_at, $2)
      WHERE id = $1
      RETURNING *`,
    [id, nowIso()]
  );
  return mapExportJob(rows[0]);
}

export async function claimNextPendingExportJob(): Promise<ExportJobEntity | null> {
  const { rows } = await getPool().query(
    `UPDATE export_jobs
        SET status = 'running',
            started_at = COALESCE(started_at, $1),
            error_message = NULL
      WHERE id = (
        SELECT id
          FROM export_jobs
         WHERE status = 'pending'
         ORDER BY created_at ASC
         FOR UPDATE SKIP LOCKED
         LIMIT 1
      )
      RETURNING *`,
    [nowIso()]
  );
  return rows[0] ? mapExportJob(rows[0]) : null;
}

export async function resetStaleRunningExportJobs(cutoffIso: string): Promise<number> {
  const { rowCount } = await getPool().query(
    `UPDATE export_jobs
        SET status = 'pending',
            error_message = 'reset from stale running state'
      WHERE status = 'running'
        AND started_at IS NOT NULL
        AND started_at < $1`,
    [cutoffIso]
  );
  return rowCount || 0;
}

export async function countExportJobsByStatus(): Promise<Record<ExportJobEntity["status"], number>> {
  const { rows } = await getPool().query("SELECT status, COUNT(*) AS count FROM export_jobs GROUP BY status");
  const counts: Record<ExportJobEntity["status"], number> = {
    pending: 0,
    running: 0,
    completed: 0,
    failed: 0
  };
  for (const row of rows) {
    if (row.status in counts) {
      counts[row.status as ExportJobEntity["status"]] = Number(row.count || 0);
    }
  }
  return counts;
}

export async function completeExportJob(
  id: string,
  payload: {
    result_content_type: string;
    result_filename: string;
    result_body?: string | null;
    result_storage_path?: string | null;
    result_size_bytes?: number;
    expires_at?: string;
  }
): Promise<ExportJobEntity> {
  const { rows } = await getPool().query(
    `UPDATE export_jobs
        SET status = 'completed',
            result_content_type = $2,
            result_filename = $3,
            result_body = $4,
            result_storage_path = $5,
            result_size_bytes = $6,
            expires_at = $7,
            completed_at = $8
      WHERE id = $1
      RETURNING *`,
    [
      id,
      payload.result_content_type,
      payload.result_filename,
      payload.result_body || null,
      payload.result_storage_path || null,
      payload.result_size_bytes || null,
      payload.expires_at || null,
      nowIso()
    ]
  );
  return mapExportJob(rows[0]);
}

export async function markExpiredExportJobs(cutoffIso: string): Promise<ExportJobEntity[]> {
  const { rows } = await getPool().query(
    `UPDATE export_jobs
        SET result_body = NULL,
            result_storage_path = NULL,
            error_message = COALESCE(error_message, 'export result expired')
      WHERE status = 'completed'
        AND expires_at IS NOT NULL
        AND expires_at < $1
        AND (result_body IS NOT NULL OR result_storage_path IS NOT NULL)
      RETURNING *`,
    [cutoffIso]
  );
  return rows.map(mapExportJob);
}

export async function failExportJob(id: string, error: string): Promise<ExportJobEntity> {
  const { rows } = await getPool().query(
    `UPDATE export_jobs
        SET status = 'failed',
            error_message = $2,
            completed_at = $3
      WHERE id = $1
      RETURNING *`,
    [id, error, nowIso()]
  );
  return mapExportJob(rows[0]);
}

export function toExportJobPayload(job: ExportJobEntity) {
  return {
    id: job.id,
    job_type: job.job_type,
    status: job.status,
    requested_by: job.requested_by,
    request_json: job.request_json,
    result_content_type: job.result_content_type,
    result_filename: job.result_filename,
    result_size_bytes: job.result_size_bytes,
    expires_at: job.expires_at,
    error_message: job.error_message,
    created_at: job.created_at,
    started_at: job.started_at,
    completed_at: job.completed_at,
    download_url: job.status === "completed" && (job.result_body || job.result_storage_path) ? `/admin/exports/${job.id}/download` : null
  };
}
