import cron from "node-cron";
import fs from "fs";
import path from "path";
import { storage } from "../storage";
import { backupDatabaseToPath } from "../services/databaseBackupService";
import { cronJobLogStorage } from "../storage/cron-job-log-storage";
import { readSyncConfig, writeSyncConfig } from "../services/networkSyncConfigService";
import { runNetworkSync, runNetworkFilesSync } from "../services/networkSyncService";
import { readRcloneConfig, writeRcloneConfig } from "../services/rcloneConfigService";
import { runRclonePull } from "../services/rcloneService";
import { importLatestBackup } from "../services/autoImportService";
import { runMhBatchPaymentCheck } from "../services/mhBatchPaymentService";

// Local backup folder in the app root (apps/Backend/backups)
const LOCAL_BACKUP_DIR = path.resolve(process.cwd(), "backups");

// Name of the USB backup subfolder the user creates on their drive
const USB_BACKUP_FOLDER_NAME = "USB Backup";

const MAX_BACKUPS = 15;

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

function pruneOldBackups(dir: string) {
  try {
    const files = fs.readdirSync(dir)
      .filter((f) => f.endsWith(".sql") || f.endsWith(".zip"))
      .map((f) => ({ name: f, mtime: fs.statSync(path.join(dir, f)).mtimeMs }))
      .sort((a, b) => b.mtime - a.mtime);

    const toDelete = files.slice(MAX_BACKUPS);
    for (const file of toDelete) {
      fs.unlinkSync(path.join(dir, file.name));
      console.log(`🗑️ Pruned old backup: ${file.name}`);
    }
  } catch (err) {
    console.warn("Failed to prune old backups:", err);
  }
}

async function getAdminUser() {
  const batchSize = 100;
  let offset = 0;
  while (true) {
    const users = await storage.getUsers(batchSize, offset);
    if (!users || users.length === 0) break;
    const admin = users.find((u) => u.username === "admin");
    if (admin) return admin;
    offset += batchSize;
  }
  return null;
}

export const startBackupCron = () => {
  // ============================================================
  // Every hour — Local automatic backup (runs when hour matches setting)
  // ============================================================
  cron.schedule("0 * * * *", async () => {
    const admin = await getAdminUser();
    if (!admin) return;
    if (!admin.autoBackupEnabled) return;

    const currentHour = new Date().getHours();
    const backupHour = admin.autoBackupHour ?? 20;
    if (currentHour !== backupHour) return;

    console.log(`🔄 [${backupHour}:00] Running local auto-backup...`);
    ensureLocalBackupDir();

    const startedAt = new Date();
    const log = await cronJobLogStorage.createJobLog("local-backup", startedAt);

    try {
      const filename = `dental_backup_${Date.now()}.zip`;
      await backupDatabaseToPath({ destinationPath: LOCAL_BACKUP_DIR, filename });
      pruneOldBackups(LOCAL_BACKUP_DIR);
      await storage.createBackup(admin.id);
      await storage.deleteNotificationsByType(admin.id, "BACKUP");
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
      console.log(`✅ Local backup done → ${filename}`);
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : String(err);
      console.error("Local backup failed:", err);
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
      await storage.createNotification(
        admin.id,
        "BACKUP",
        "❌ Automatic backup failed. Please check the server backup folder."
      );
    }
  });

  // ============================================================
  // Every hour — USB backup (runs when hour matches setting)
  // ============================================================
  cron.schedule("0 * * * *", async () => {
    const admin = await getAdminUser();
    if (!admin) return;
    if (!admin.usbBackupEnabled) return;

    const currentHour = new Date().getHours();
    const backupHour = admin.usbBackupHour ?? 21;
    if (currentHour !== backupHour) return;

    console.log(`🔄 [${backupHour}:00] Running USB backup...`);

    const startedAt = new Date();
    const log = await cronJobLogStorage.createJobLog("usb-backup", startedAt);

    const destination = await storage.getActiveBackupDestination(admin.id);
    if (!destination) {
      const errorMessage = "No backup destination configured.";
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
      await storage.createNotification(
        admin.id,
        "BACKUP",
        "❌ USB backup failed: no backup destination configured."
      );
      return;
    }

    if (!fs.existsSync(destination.path)) {
      const errorMessage = "Backup destination drive not found or disconnected.";
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
      await storage.createNotification(
        admin.id,
        "BACKUP",
        "❌ USB backup failed: backup drive not found. Make sure the USB drive is connected."
      );
      return;
    }

    const usbBackupPath = path.join(destination.path, USB_BACKUP_FOLDER_NAME);

    if (!fs.existsSync(usbBackupPath)) {
      fs.mkdirSync(usbBackupPath, { recursive: true });
    }

    try {
      const filename = `dental_backup_usb_${Date.now()}.zip`;
      await backupDatabaseToPath({ destinationPath: usbBackupPath, filename });
      pruneOldBackups(usbBackupPath);
      await storage.createBackup(admin.id);
      await storage.deleteNotificationsByType(admin.id, "BACKUP");
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
      console.log(`✅ USB backup done → ${usbBackupPath}/${filename}`);
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : String(err);
      console.error("USB backup failed:", err);
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
      await storage.createNotification(
        admin.id,
        "BACKUP",
        "❌ USB backup failed. Please check the USB drive and try again."
      );
    }
  });

  // ============================================================
  // Every hour — Rclone backup (runs only when hour matches config)
  // ============================================================
  cron.schedule("0 * * * *", async () => {
    const rcloneConfig = readRcloneConfig();
    if (!rcloneConfig.receiverEnabled || !rcloneConfig.sourceIp) return;

    const currentHour = new Date().getHours();
    if (currentHour !== rcloneConfig.receiverSyncHour) return;

    console.log(`[${rcloneConfig.receiverSyncHour}:00] Running rclone pull from ${rcloneConfig.sourceIp}...`);

    const admin = await getAdminUser();
    const startedAt = new Date();
    const log = await cronJobLogStorage.createJobLog("rclone-backup", startedAt);

    try {
      await runRclonePull();
      writeRcloneConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "success", lastSyncError: null });
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
      console.log(`Rclone backup complete.`);
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : String(err);
      console.error("Rclone backup failed:", err);
      writeRcloneConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "failed", lastSyncError: errorMessage });
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
      if (admin) {
        await storage.createNotification(
          admin.id,
          "BACKUP",
          `Rclone backup failed: ${errorMessage}`
        );
      }
    }
  });

  // ============================================================
  // Every hour — Auto-import latest backup (runs when hour matches config)
  // ============================================================
  cron.schedule("0 * * * *", async () => {
    const importConfig = readRcloneConfig();
    if (!importConfig.autoImportEnabled) return;

    const currentHour = new Date().getHours();
    if (currentHour !== importConfig.autoImportHour) return;

    console.log(`[${importConfig.autoImportHour}:00] Running auto-import of latest backup...`);

    const admin = await getAdminUser();
    const startedAt = new Date();
    const log = await cronJobLogStorage.createJobLog("auto-import", startedAt);

    try {
      await importLatestBackup();
      writeRcloneConfig({ lastImportAt: new Date().toISOString(), lastImportStatus: "success", lastImportError: null });
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
      console.log(`Auto-import complete.`);
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : String(err);
      console.error("Auto-import failed:", err);
      writeRcloneConfig({ lastImportAt: new Date().toISOString(), lastImportStatus: "failed", lastImportError: errorMessage });
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
      if (admin) {
        await storage.createNotification(
          admin.id,
          "BACKUP",
          `Auto-import failed: ${errorMessage}`
        );
      }
    }
  });

  // ============================================================
  // Every hour — Network sync (runs only when hour matches config)
  // ============================================================
  cron.schedule("0 * * * *", async () => {
    const config = readSyncConfig();
    if (!config.enabled || !config.sourceUrl || !config.apiKey) return;

    const currentHour = new Date().getHours();
    if (currentHour !== config.syncHour) return;

    console.log(`🔄 [${config.syncHour}:00] Running network sync from ${config.sourceUrl}...`);

    const admin = await getAdminUser();
    const startedAt = new Date();
    const log = await cronJobLogStorage.createJobLog("network-sync", startedAt);

    try {
      await runNetworkSync(config.sourceUrl, config.apiKey);
      await runNetworkFilesSync(config.sourceUrl, config.apiKey);
      writeSyncConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "success", lastSyncError: null });
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "success", new Date());
      console.log(`✅ Network sync complete (database + uploads).`);
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : String(err);
      console.error("Network sync failed:", err);
      writeSyncConfig({ lastSyncAt: new Date().toISOString(), lastSyncStatus: "failed", lastSyncError: errorMessage });
      await cronJobLogStorage.completeJobLog(log.id, log.jobName, startedAt, "failed", new Date(), errorMessage);
      if (admin) {
        await storage.createNotification(
          admin.id,
          "BACKUP",
          `❌ Network sync failed: ${errorMessage}`
        );
      }
    }
  });

  // ============================================================
  // Every hour — Auto MH payment check (runs when day-of-week and hour match setting)
  // ============================================================
  cron.schedule("0 * * * *", async () => {
    const admin = await getAdminUser();
    if (!admin) return;
    if (!admin.autoMhCheckEnabled) return;

    const now = new Date();
    const currentDayOfWeek = now.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday
    const currentHour = now.getHours();

    const targetDay = admin.autoMhCheckDayOfWeek ?? 1;
    const targetHour = admin.autoMhCheckHour ?? 13;

    if (currentDayOfWeek !== targetDay || currentHour !== targetHour) return;

    console.log(`🔄 [Auto MH Check] Running scheduled MH batch payment check...`);
    const startedAt = new Date();
    const log = await cronJobLogStorage.createJobLog("auto-mh-check", startedAt);

    try {
      // Check the past 7 days
      const toDate = now.toISOString().split("T")[0];
      const fromDateObj = new Date(now);
      fromDateObj.setDate(fromDateObj.getDate() - 7);
      const fromDate = fromDateObj.toISOString().split("T")[0];

      const result = await runMhBatchPaymentCheck(admin.id, fromDate, toDate);
      await cronJobLogStorage.completeJobLog(log.id, "success", new Date());
      console.log(`✅ Auto MH Check done: ${result.importSummary ?? result.message ?? "complete"}`);
    } catch (err) {
      const errorMessage = err instanceof Error ? err.message : String(err);
      console.error("Auto MH Check failed:", err);
      await cronJobLogStorage.completeJobLog(log.id, "failed", new Date(), errorMessage);
      await storage.createNotification(
        admin.id,
        "BACKUP",
        `❌ Auto MH payment check failed: ${errorMessage}`
      );
    }
  });
};
