import * as fs from 'fs';
import * as path from 'path';

export type BackgroundJobType = 'generate-large-report' | 'archive-old-invoices';
export type BackgroundJobStatus = 'queued' | 'processing' | 'completed' | 'failed';

export type BackgroundJob = {
  id: string;
  type: BackgroundJobType;
  status: BackgroundJobStatus;
  payload: Record<string, unknown>;
  createdAt: string;
  startedAt?: string;
  finishedAt?: string;
  error?: string;
  result?: Record<string, unknown>;
};

const dbPath = path.join(process.cwd(), 'db');
const jobsFilePath = path.join(dbPath, 'background_jobs.json');

function ensureDbPath() {
  if (!fs.existsSync(dbPath)) {
    fs.mkdirSync(dbPath, { recursive: true });
  }
}

function readJobs(): BackgroundJob[] {
  ensureDbPath();
  if (!fs.existsSync(jobsFilePath)) {
    fs.writeFileSync(jobsFilePath, '[]', 'utf-8');
    return [];
  }

  try {
    const raw = fs.readFileSync(jobsFilePath, 'utf-8');
    if (!raw.trim()) return [];
    const parsed = JSON.parse(raw);
    return Array.isArray(parsed) ? parsed as BackgroundJob[] : [];
  } catch {
    return [];
  }
}

function writeJobs(jobs: BackgroundJob[]): void {
  ensureDbPath();
  fs.writeFileSync(jobsFilePath, JSON.stringify(jobs, null, 2), 'utf-8');
}

function nextJobId(): string {
  return `job-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}

export function getBackgroundJobs(): BackgroundJob[] {
  return readJobs().sort((a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime());
}

export function enqueueBackgroundJob(type: BackgroundJobType, payload: Record<string, unknown>): BackgroundJob {
  const jobs = readJobs();
  const job: BackgroundJob = {
    id: nextJobId(),
    type,
    status: 'queued',
    payload,
    createdAt: new Date().toISOString(),
  };
  jobs.push(job);
  writeJobs(jobs);
  return job;
}

export function claimNextBackgroundJob(): BackgroundJob | null {
  const jobs = readJobs();
  const nextIndex = jobs.findIndex((job) => job.status === 'queued');
  if (nextIndex === -1) return null;

  jobs[nextIndex] = {
    ...jobs[nextIndex],
    status: 'processing',
    startedAt: new Date().toISOString(),
    error: undefined,
  };

  writeJobs(jobs);
  return jobs[nextIndex];
}

export function completeBackgroundJob(id: string, result?: Record<string, unknown>): void {
  const jobs = readJobs();
  const index = jobs.findIndex((job) => job.id === id);
  if (index === -1) return;

  jobs[index] = {
    ...jobs[index],
    status: 'completed',
    finishedAt: new Date().toISOString(),
    result,
    error: undefined,
  };

  writeJobs(jobs);
}

export function failBackgroundJob(id: string, error: string): void {
  const jobs = readJobs();
  const index = jobs.findIndex((job) => job.id === id);
  if (index === -1) return;

  jobs[index] = {
    ...jobs[index],
    status: 'failed',
    finishedAt: new Date().toISOString(),
    error,
  };

  writeJobs(jobs);
}

function processArchiveOldInvoices(payload: Record<string, unknown>): Record<string, unknown> {
  const months = Number(payload.months ?? 24);
  const thresholdDate = new Date();
  thresholdDate.setMonth(thresholdDate.getMonth() - months);

  const invoicesPath = path.join(dbPath, 'invoices.json');
  const archivePath = path.join(dbPath, 'archives');
  if (!fs.existsSync(archivePath)) {
    fs.mkdirSync(archivePath, { recursive: true });
  }

  if (!fs.existsSync(invoicesPath)) {
    return { archivedCount: 0, reason: 'invoices file not found' };
  }

  const invoicesRaw = fs.readFileSync(invoicesPath, 'utf-8');
  const invoices = JSON.parse(invoicesRaw || '[]') as Array<Record<string, unknown>>;
  const activeInvoices: Array<Record<string, unknown>> = [];
  const archivedInvoices: Array<Record<string, unknown>> = [];

  for (const invoice of invoices) {
    const invoiceDate = new Date(String(invoice.date || invoice.createdAt || Date.now()));
    if (invoiceDate < thresholdDate) {
      archivedInvoices.push(invoice);
    } else {
      activeInvoices.push(invoice);
    }
  }

  if (archivedInvoices.length === 0) {
    return { archivedCount: 0 };
  }

  const archiveFile = path.join(archivePath, `invoices-archive-${Date.now()}.json`);
  fs.writeFileSync(archiveFile, JSON.stringify(archivedInvoices, null, 2), 'utf-8');
  fs.writeFileSync(invoicesPath, JSON.stringify(activeInvoices, null, 2), 'utf-8');

  return {
    archivedCount: archivedInvoices.length,
    archiveFile: path.basename(archiveFile),
  };
}

function processLargeReport(payload: Record<string, unknown>): Record<string, unknown> {
  const reportName = String(payload.reportName || 'generic-report');
  const reportPath = path.join(dbPath, 'backups', `${reportName}-${Date.now()}.json`);
  const result = {
    reportName,
    generatedAt: new Date().toISOString(),
    filters: payload.filters ?? {},
    note: 'Background job stub generated this report envelope. Integrate business aggregation here.',
  };

  const reportDir = path.dirname(reportPath);
  if (!fs.existsSync(reportDir)) {
    fs.mkdirSync(reportDir, { recursive: true });
  }
  fs.writeFileSync(reportPath, JSON.stringify(result, null, 2), 'utf-8');

  return {
    reportFile: path.basename(reportPath),
    reportName,
  };
}

export function processNextBackgroundJob(): BackgroundJob | null {
  const job = claimNextBackgroundJob();
  if (!job) return null;

  try {
    let result: Record<string, unknown>;
    if (job.type === 'archive-old-invoices') {
      result = processArchiveOldInvoices(job.payload);
    } else {
      result = processLargeReport(job.payload);
    }

    completeBackgroundJob(job.id, result);
    return {
      ...job,
      status: 'completed',
      finishedAt: new Date().toISOString(),
      result,
    };
  } catch (error) {
    failBackgroundJob(job.id, error instanceof Error ? error.message : 'Unknown error');
    return {
      ...job,
      status: 'failed',
      finishedAt: new Date().toISOString(),
      error: error instanceof Error ? error.message : 'Unknown error',
    };
  }
}
