// import required so Node type-strips this .cts (it has no other import).
import type { Db } from '../types/db';

const cron = require('node-cron');
const { getSetting, setSetting } = require('../db/database.cts');
const { applyScheduledRetention, createBackup } = require('./backupService.cts');

interface ScheduleSettings {
  enabled: boolean;
  frequency: string;
  time: string;
  retention_count: number;
}

let task: any = null;
let nextRunAt: string | null = null;
let running = false;

function parseBool(value: unknown): boolean {
  return value === true || value === 'true';
}

function validateScheduleSettings(input: any = {}): ScheduleSettings {
  const enabled = parseBool(input.enabled);
  const frequency = input.frequency || 'daily';
  const time = input.time || '02:00';
  const retentionCount = parseInt(input.retention_count ?? '2', 10);

  if (!['daily', 'weekly'].includes(frequency)) {
    const err = new Error('frequency must be daily or weekly') as any;
    err.status = 400;
    throw err;
  }
  if (!/^\d{2}:\d{2}$/.test(time)) {
    const err = new Error('time must use HH:MM format') as any;
    err.status = 400;
    throw err;
  }

  const [hour, minute] = time.split(':').map(Number);
  if (hour < 0 || hour > 23 || minute < 0 || minute > 59) {
    const err = new Error('time must be a valid 24-hour time') as any;
    err.status = 400;
    throw err;
  }
  if (!Number.isInteger(retentionCount) || retentionCount < 1 || retentionCount > 365) {
    const err = new Error('retention_count must be between 1 and 365') as any;
    err.status = 400;
    throw err;
  }

  return { enabled, frequency, time, retention_count: retentionCount };
}

function readSettings(): ScheduleSettings {
  return validateScheduleSettings({
    enabled: getSetting('backup_schedule_enabled') === 'true',
    frequency: getSetting('backup_schedule_frequency') || 'daily',
    time: getSetting('backup_schedule_time') || '02:00',
    retention_count: getSetting('backup_schedule_retention_count') || '2',
  });
}

function getLastRunAt(): string | null {
  return getSetting('backup_schedule_last_run_at') || null;
}

function getLastError(): string | null {
  return getSetting('backup_schedule_last_error') || null;
}

function computeNextRun(
  settings: ScheduleSettings = readSettings(),
  from: Date = new Date(),
): string | null {
  if (!settings.enabled) return null;
  const [hour, minute] = settings.time.split(':').map(Number);
  const next = new Date(from);
  next.setHours(hour, minute, 0, 0);

  if (settings.frequency === 'weekly') {
    while (next <= from || next.getDay() !== 0) next.setDate(next.getDate() + 1);
    return next.toISOString();
  }

  if (next <= from) next.setDate(next.getDate() + 1);
  return next.toISOString();
}

function cronExpression(settings: ScheduleSettings): string {
  const [hour, minute] = settings.time.split(':').map(Number);
  return settings.frequency === 'weekly' ? `${minute} ${hour} * * 0` : `${minute} ${hour} * * *`;
}

async function runScheduledBackupNow(): Promise<any> {
  if (running) {
    const err = new Error('Scheduled backup is already running') as any;
    err.status = 409;
    throw err;
  }

  running = true;
  try {
    const settings = readSettings();
    const backup = await createBackup('scheduled-backup');
    applyScheduledRetention(settings.retention_count);
    setSetting('backup_schedule_last_run_at', new Date().toISOString());
    setSetting('backup_schedule_last_error', '');
    nextRunAt = computeNextRun(settings);
    return backup;
  } catch (err: any) {
    setSetting('backup_schedule_last_error', err.message || 'Scheduled backup failed');
    throw err;
  } finally {
    running = false;
  }
}

function stop(): void {
  if (task) task.stop();
  task = null;
}

function reloadSchedule() {
  stop();
  const settings = readSettings();
  nextRunAt = computeNextRun(settings);

  if (!settings.enabled) return getScheduleStatus();

  task = cron.schedule(cronExpression(settings), () => {
    runScheduledBackupNow().catch((err: any) => {
      console.error('[backupScheduler] Scheduled backup failed:', err.message);
    });
  });

  return getScheduleStatus();
}

function saveSettings(input: any) {
  const settings = validateScheduleSettings(input);
  setSetting('backup_schedule_enabled', settings.enabled ? 'true' : 'false');
  setSetting('backup_schedule_frequency', settings.frequency);
  setSetting('backup_schedule_time', settings.time);
  setSetting('backup_schedule_retention_count', String(settings.retention_count));
  return reloadSchedule();
}

function getScheduleStatus() {
  const settings = readSettings();
  return {
    ok: !getLastError(),
    enabled: settings.enabled,
    running,
    frequency: settings.frequency,
    time: settings.time,
    retention_count: settings.retention_count,
    last_run_at: getLastRunAt(),
    next_run_at: nextRunAt ?? computeNextRun(settings),
    last_error: getLastError(),
  };
}

function start() {
  return reloadSchedule();
}

module.exports = {
  computeNextRun,
  getScheduleStatus,
  reloadSchedule,
  runScheduledBackupNow,
  saveSettings,
  start,
  validateScheduleSettings,
};
