import { randomUUID } from 'node:crypto';
import pino from 'pino';
import { loadEnv } from '../config/env.js';
import { createDatabase } from '../db/database.js';
import { migrate } from '../db/migrations.js';
import { FinancialImportService } from '../services/excel-import-service.js';
import { ReportService } from '../services/report-service.js';

const env = loadEnv();
const logger = pino({ level: env.LOG_LEVEL });
const db = await createDatabase(env);
await migrate(db);
const workerId = `${process.pid}-${randomUUID()}`;
const imports = new FinancialImportService(db);
const reports = new ReportService(db);
let stopping = false;
process.on('SIGTERM', () => { stopping = true; });
process.on('SIGINT', () => { stopping = true; });

const wait = (milliseconds: number) => new Promise((resolve) => setTimeout(resolve, milliseconds));

async function claimJob() {
  return db.transaction(async (tx) => {
    const result = await tx.query<{ id: string; type: string; payload: Record<string, any>; attempts: number; max_attempts: number }>(
      `SELECT id, type, payload, attempts, max_attempts FROM jobs
       WHERE status = 'queued' AND available_at <= now() ORDER BY created_at
       FOR UPDATE SKIP LOCKED LIMIT 1`
    );
    if (!result.rowCount) return null;
    const job = result.rows[0]!;
    await tx.query(`UPDATE jobs SET status = 'processing', locked_at = now(), locked_by = $2, attempts = attempts + 1 WHERE id = $1`, [job.id, workerId]);
    return job;
  });
}

async function processJob(job: { id: string; type: string; payload: Record<string, any> }) {
  if (job.type === 'financial_import') {
    const importId = String(job.payload.importId);
    const parsed = await imports.parseStored(importId);
    await imports.confirm(importId, parsed, { actorRole: 'queue_worker' });
    return;
  }
  if (job.type === 'report_export') {
    const startUtc = new Date(job.payload.startUtc); const endUtc = new Date(job.payload.endUtc);
    await reports.save({ startUtc, endUtc, label: String(job.payload.label) }, String(job.payload.filename));
    return;
  }
  throw new Error(`نوع job ناشناخته است: ${job.type}`);
}

while (!stopping) {
  const job = await claimJob();
  if (!job) { await wait(1000); continue; }
  try {
    await processJob(job);
    await db.query(`UPDATE jobs SET status = 'completed', completed_at = now(), locked_at = NULL, locked_by = NULL WHERE id = $1`, [job.id]);
    logger.info({ jobId: job.id, type: job.type }, 'job completed');
  } catch (error) {
    const attempt = job.attempts + 1;
    const failed = attempt >= job.max_attempts;
    await db.query(
      `UPDATE jobs SET status = $2, available_at = now() + ($3::text || ' seconds')::interval,
        locked_at = NULL, locked_by = NULL, last_error = $4 WHERE id = $1`,
      [job.id, failed ? 'failed' : 'queued', Math.min(300, 2 ** attempt * 5), String((error as Error).message).slice(0, 2000)]
    );
    logger.error({ err: error, jobId: job.id, failed }, 'job failed');
  }
}
await db.close();
