import { createDbService } from '@/server/services/db.js';
import { listCronRuns } from '@/server/db/queries/cron-runs.js';
import type { AlertSeverity } from '@/server/cron/settlement-monitor.js';
import { redactError } from '../redact.js';
import type { CheckAdapter, CheckContext, CheckResult } from '../types.js';
import type { CronRunRecord } from '@/server/db/schema.js';

export interface HeartbeatConfig {
  stalenessFactor: number;
}

export function evaluateHeartbeat(
  rows: CronRunRecord[],
  cfg: HeartbeatConfig,
  now: Date,
): { severity: AlertSeverity; detail: string } {
  const offenders: string[] = [];
  for (const r of rows) {
    const success = r.lastSuccessAt ? new Date(r.lastSuccessAt) : null;
    const error = r.lastErrorAt ? new Date(r.lastErrorAt) : null;
    if (error && (!success || error.getTime() > success.getTime())) {
      offenders.push(`${r.cronKey}: errored (${r.lastError ?? '?'})`);
      continue;
    }
    if (success) {
      const staleMs = now.getTime() - success.getTime();
      if (staleMs > r.expectedIntervalMinutes * 60_000 * cfg.stalenessFactor) {
        offenders.push(
          `${r.cronKey}: stale ${Math.floor(staleMs / 60_000)}m (expected every ${r.expectedIntervalMinutes}m)`,
        );
      }
    }
  }
  if (offenders.length === 0)
    return { severity: 'info', detail: `${rows.length} cron handlers healthy` };
  return { severity: 'page', detail: offenders.join('; ') };
}

export const cronHeartbeat: CheckAdapter = {
  key: 'cron_heartbeat',
  title: 'Cron heartbeat',
  defaultConfig: { stalenessFactor: 2 },
  defaultIntervalHours: 1,
  async run(ctx: CheckContext): Promise<CheckResult> {
    const cfg = { stalenessFactor: Number(ctx.config.stalenessFactor ?? 2) };
    try {
      const db = createDbService({ DATABASE_URL: ctx.env.DATABASE_URL });
      const rows = await listCronRuns(db);
      const ev = evaluateHeartbeat(rows, cfg, ctx.now);
      return {
        key: this.key,
        severity: ev.severity,
        title: this.title,
        detail: ev.detail,
        metrics: { handlers: rows.length },
      };
    } catch (err) {
      return {
        key: this.key,
        severity: 'warn',
        degraded: true,
        title: this.title,
        detail: redactError(err, 'check threw'),
      };
    }
  },
};
