import pool from '../config/database';
import buchhaltungService from '../services/buchhaltung.service';
import logger from '../utils/logger';

const INTERVAL_MS = 60 * 60 * 1000; // hourly
let jobInterval: ReturnType<typeof setInterval> | null = null;
let isRunning = false;

/** Advance a date by N months, then apply ausfuehrungstag. */
function advanceDate(base: Date, months: number, ausfuehrungstag: 'erster' | 'mitte' | 'letzter'): Date {
  const d = new Date(base);
  d.setMonth(d.getMonth() + months);
  if (ausfuehrungstag === 'erster') {
    d.setDate(1);
  } else if (ausfuehrungstag === 'mitte') {
    d.setDate(15);
  } else {
    // last day of the month
    d.setMonth(d.getMonth() + 1, 0);
  }
  return d;
}

async function runRecurringCheck(): Promise<void> {
  if (isRunning) {
    logger.warn('BuchhaltungRecurringJob: previous run still in progress — skipping');
    return;
  }
  isRunning = true;
  try {
    const dueResult = await pool.query(
      `SELECT * FROM buchhaltung_wiederkehrend WHERE aktiv = TRUE AND naechste_ausfuehrung <= CURRENT_DATE`
    );

    if (dueResult.rows.length === 0) {
      isRunning = false;
      return;
    }

    const haushaltsjahr = await buchhaltungService.getCurrentHaushaltsjahr();
    if (!haushaltsjahr) {
      logger.warn('BuchhaltungRecurringJob: no open fiscal year — skipping');
      isRunning = false;
      return;
    }

    let processed = 0;
    for (const template of dueResult.rows) {
      try {
        // Create transaction directly to set wiederkehrend_id
        const txResult = await pool.query(
          `INSERT INTO buchhaltung_transaktionen
             (haushaltsjahr_id, konto_id, bankkonto_id, typ, betrag, datum, beschreibung,
              empfaenger_auftraggeber, erstellt_von, wiederkehrend_id, status)
           VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, 'entwurf')
           RETURNING *`,
          [
            haushaltsjahr.id,
            template.konto_id,
            template.bankkonto_id,
            template.typ,
            template.betrag,
            template.naechste_ausfuehrung,
            template.beschreibung,
            template.empfaenger_auftraggeber,
            template.erstellt_von,
            template.id,
          ]
        );
        const tx = txResult.rows[0];
        if (tx) {
          await buchhaltungService.logAudit(tx.id, 'erstellt_wiederkehrend', { wiederkehrend_id: template.id }, template.erstellt_von);
        }

        // Advance naechste_ausfuehrung
        const monthsMap: Record<string, number> = {
          monatlich:     1,
          quartalsweise: 3,
          halbjaehrlich: 6,
          jaehrlich:     12,
        };
        const months = monthsMap[template.intervall] ?? 1;
        const nextDate = advanceDate(new Date(template.naechste_ausfuehrung), months, template.ausfuehrungstag);
        const nextDateStr = nextDate.toISOString().split('T')[0];

        await pool.query(
          `UPDATE buchhaltung_wiederkehrend SET naechste_ausfuehrung = $1 WHERE id = $2`,
          [nextDateStr, template.id]
        );
        processed++;
      } catch (templateErr) {
        logger.error('BuchhaltungRecurringJob: failed to process template', {
          id: template.id,
          error: templateErr instanceof Error ? templateErr.message : String(templateErr),
        });
      }
    }

    logger.info(`BuchhaltungRecurringJob: processed ${processed} recurring transactions`);
  } catch (error) {
    logger.error('BuchhaltungRecurringJob: unexpected error', {
      error: error instanceof Error ? error.message : String(error),
    });
  } finally {
    isRunning = false;
  }
}

export function startBuchhaltungRecurringJob(): void {
  if (jobInterval !== null) {
    logger.warn('BuchhaltungRecurringJob: already running — skipping duplicate start');
    return;
  }
  // Delay initial run to let migrations settle
  setTimeout(() => runRecurringCheck(), 60_000);
  jobInterval = setInterval(() => runRecurringCheck(), INTERVAL_MS);
  logger.info('Buchhaltung recurring job scheduled (hourly)');
}

export function stopBuchhaltungRecurringJob(): void {
  if (jobInterval !== null) {
    clearInterval(jobInterval);
    jobInterval = null;
  }
  logger.info('Buchhaltung recurring job stopped');
}
