/**
 * auto-scheduler.js — Scheduler timer untuk auto-refresh token
 * Lebih stabil untuk interval custom dan lebih mudah dites daripada cron.
 */

import { refreshExistingAccountToken, stopActiveBot } from "./auto-token.js";
import * as Accounts from "./account-store.js";

let creatorTimer = null;
let creatorEnabled = false;
let creatorIntervalMinutes = 45;
let creatorNextRunAt = null;

let reviveTimer = null;
let reviveEnabled = false;
let reviveIntervalMinutes = 15;
let reviveNextRunAt = null;

let isRunning = false;
let lastRunResult = null;
let lastRunAt = null;
let consecutiveFailures = 0;
let testTimer = null;
let testRunAt = null;

/** Global SSE client registry */
const sseClients = new Set();

export function addSseClient(res) {
  sseClients.add(res);
  res.on("close", () => sseClients.delete(res));
}

export function broadcastLog(event) {
  for (const res of sseClients) {
    try {
      let filteredEvent = event;
      if (res.adminRole !== "owner") {
        if (event && typeof event === "object") {
          filteredEvent = JSON.parse(JSON.stringify(event));
          if (filteredEvent.msg && typeof filteredEvent.msg === "string") {
            filteredEvent.msg = filteredEvent.msg
              .replace(/https?:\/\/(?:www\.)?figma\.com/gi, "[masked]")
              .replace(/https?:\/\/weave\.figma\.com/gi, "[masked]")
              .replace(/figma\.com/gi, "[masked]")
              .replace(/weave\.figma\.com/gi, "[masked]")
              .replace(/figma/gi, "System")
              .replace(/weave/gi, "Engine");
          }
        }
      }
      res.write(`data: ${JSON.stringify(filteredEvent)}\n\n`);
    } catch {
      sseClients.delete(res);
    }
  }
}

function clearCreatorTimer() {
  if (creatorTimer) {
    clearTimeout(creatorTimer);
    creatorTimer = null;
  }
}

function clearReviveTimer() {
  if (reviveTimer) {
    clearTimeout(reviveTimer);
    reviveTimer = null;
  }
}

function clearTestTimer() {
  if (testTimer) {
    clearTimeout(testTimer);
    testTimer = null;
  }
  testRunAt = null;
}

function normalizeIntervalMinutes(intervalMinutes) {
  const mins = Number(intervalMinutes);
  if (!Number.isFinite(mins) || mins <= 0) {
    throw new Error("Interval scheduler harus lebih besar dari 0 menit.");
  }
  if (mins > 1440) {
    throw new Error("Interval scheduler maksimal 1440 menit.");
  }
  return Math.round(mins);
}

function scheduleNextCreatorRun(delayMs = creatorIntervalMinutes * 60 * 1000) {
  clearCreatorTimer();
  if (!creatorEnabled) {
    creatorNextRunAt = null;
    return;
  }

  creatorNextRunAt = new Date(Date.now() + delayMs).toISOString();
  creatorTimer = setTimeout(async () => {
    creatorTimer = null;
    creatorNextRunAt = null;
    broadcastLog({ type: "info", msg: "⏰ Auto-Scheduler Creator trigger: membuat akun baru..." });
    await triggerAutoToken(false, { source: "scheduler", mode: "create_only" });
    if (creatorEnabled) {
      scheduleNextCreatorRun();
    }
  }, delayMs);
}

function scheduleNextReviveRun(delayMs = reviveIntervalMinutes * 60 * 1000) {
  clearReviveTimer();
  if (!reviveEnabled) {
    reviveNextRunAt = null;
    return;
  }

  reviveNextRunAt = new Date(Date.now() + delayMs).toISOString();
  reviveTimer = setTimeout(async () => {
    reviveTimer = null;
    reviveNextRunAt = null;
    broadcastLog({ type: "info", msg: "⏰ Auto-Scheduler Revive trigger: memulihkan akun expired..." });
    await triggerAutoToken(false, { source: "scheduler", mode: "revive_only" });
    if (reviveEnabled) {
      scheduleNextReviveRun();
    }
  }, delayMs);
}

/** Check if pool needs refresh (all expired or empty) */
function poolNeedsRefresh() {
  const accounts = Accounts.listAccountsSafe();
  if (accounts.length === 0) return true;
  const active = accounts.filter((a) => !a.expired);
  return active.length === 0;
}

/** Scan and login back into expired accounts with remaining runs */
async function reviveExpiredAccounts() {
  const accounts = Accounts.listAccounts();

  if (accounts.length === 0) {
    broadcastLog({ type: "info", msg: "ℹ️ Pool kosong, tidak ada akun untuk di-revive." });
    return 0;
  }

  broadcastLog({ type: "info", msg: `🔍 Memeriksa ${accounts.length} akun di pool untuk revive...` });

  const toRevive = [];
  for (const a of accounts) {
    const isExp = Accounts.isExpired(a.token) || a.expired;
    const isArchived = Boolean(a.archived);
    const isBlocked = Boolean(a.figmaBlocked || a.googleBlocked);
    const hasCredentials = !!(a.email && a.password); // Figma Sign-In butuh email + password
    const aMaxRuns = a.maxRuns || Accounts.DEFAULT_MAX_RUNS;
    const runsOk = (a.runs || 0) < aMaxRuns;
    const runsExpired = !runsOk; // reached max runs — expired

    if (isArchived) {
      broadcastLog({ type: "info", msg: `  📦 ${a.label || a.email}: ada di archive bulan ${a.archivedMonth || "-" }, skip revive` });
      continue;
    }
    if (isBlocked) {
      const reason = a.figmaBlockedReason || a.googleBlockedReason || "tidak diketahui";
      broadcastLog({ type: "warn", msg: `  🚫 ${a.label || a.email}: diblokir (${reason}), skip revive` });
      continue;
    }
    if (!isExp && !runsExpired) {
      broadcastLog({ type: "info", msg: `  ✅ ${a.label || a.email}: masih aktif (runs ${a.runs || 0}/${aMaxRuns}), skip revive` });
      continue;
    }
    if (!hasCredentials) {
      broadcastLog({ type: "warn", msg: `  ⚠️ ${a.label || a.id}: expired tapi tidak punya email/password Figma, tidak bisa revive` });
      continue;
    }
    if (!runsOk) {
      broadcastLog({ type: "warn", msg: `  ⚠️ ${a.email}: expired, runs=${a.runs}/${aMaxRuns} (max tercapai), skip` });
      continue;
    }

    broadcastLog({ type: "info", msg: `  🔄 ${a.email}: JWT expired, runs=${a.runs || 0}/${aMaxRuns}, siap untuk revive!` });
    toRevive.push(a);
  }

  if (toRevive.length === 0) {
    broadcastLog({ type: "info", msg: "ℹ️ Tidak ada akun yang memenuhi syarat untuk revive." });
    return 0;
  }

  broadcastLog({ type: "info", msg: `🔄 Mencoba revive ${toRevive.length} akun expired...` });

  let revivedCount = 0;
  for (const acc of toRevive) {
    try {
      broadcastLog({ type: "info", msg: `🔄 Revive akun: ${acc.email}...` });
      const result = await refreshExistingAccountToken(acc.id, acc.email, acc.password, (event) => broadcastLog(event));
      if (result.ok) {
        revivedCount++;
        broadcastLog({ type: "info", msg: `✅ Revive berhasil: ${acc.email}` });
      } else {
        broadcastLog({ type: "warn", msg: `⚠️ Revive gagal: ${acc.email} — ${result.error}` });
      }
    } catch (err) {
      broadcastLog({ type: "warn", msg: `⚠️ Gagal revive akun ${acc.email}: ${err.message}` });
    }
  }
  return revivedCount;
}

/** Run the auto-token flow (protected from concurrent runs) */
export async function triggerAutoToken(force = false, { source = "manual", mode = "all" } = {}) {
  if (isRunning) {
    broadcastLog({ type: "warn", msg: "⚠️ Bot sudah berjalan, tunggu selesai dulu." });
    return { ok: false, error: "Already running" };
  }

  isRunning = true;
  lastRunAt = new Date().toISOString();
  broadcastLog({ type: "start", msg: `🤖 Auto Token Bot dimulai (${source}, mode: ${mode})...` });

  try {
    let revived = 0;
    
    // Run revive if mode is all or revive_only
    if (mode === "all" || mode === "revive_only") {
      revived = await reviveExpiredAccounts();
    }

    // Stop here if revive_only
    if (mode === "revive_only") {
      isRunning = false;
      consecutiveFailures = 0;
      lastRunResult = {
        ok: true,
        msg: revived > 0
          ? `Revive selesai. ${revived} akun berhasil dihidupkan kembali.`
          : "Revive selesai. Tidak ada akun yang memenuhi syarat revive.",
      };
      broadcastLog({ type: "success", msg: `✅ ${lastRunResult.msg}` });
      return lastRunResult;
    }

    // If mode is all, check if we need to stop before creating new accounts
    if (mode === "all") {
      if (!force && !poolNeedsRefresh()) {
        broadcastLog({ type: "info", msg: "ℹ️ Pool sekarang memiliki akun aktif, selesai." });
        isRunning = false;
        consecutiveFailures = 0;
        lastRunResult = {
          ok: true,
          msg: revived > 0
            ? `Berhasil revive ${revived} akun dan pool kembali aktif.`
            : "Pool aktif, tidak perlu tindakan tambahan.",
        };
        broadcastLog({ type: "success", msg: `✅ ${lastRunResult.msg}` });
        return lastRunResult;
      }

      if (process.env.AUTO_REVIVE_ONLY === "true") {
        broadcastLog({ type: "info", msg: "ℹ️ Mode Hanya Revive (AUTO_REVIVE_ONLY) aktif. Tidak membuat akun baru." });
        isRunning = false;
        consecutiveFailures = 0;
        lastRunResult = {
          ok: true,
          msg: revived > 0
            ? `Mode Hanya Revive selesai. ${revived} akun berhasil dihidupkan kembali.`
            : "Mode Hanya Revive selesai. Tidak ada akun yang bisa di-revive.",
        };
        broadcastLog({ type: "success", msg: `✅ ${lastRunResult.msg}` });
        return lastRunResult;
      }
    }

    broadcastLog({ type: "info", msg: "⏳ Menjalankan pembuatan akun baru..." });
    broadcastLog({ type: "warn", msg: "⚠️ runFullFlow sudah deprecated. Gunakan Bulk Bot di Dashboard (Tools Akun) untuk menambah akun via Figma Sign-In." });
    isRunning = false;
    consecutiveFailures = 0;
    lastRunResult = { ok: true, msg: "Pembuatan akun baru hanya via Bulk Bot (Dashboard)." };
    broadcastLog({ type: "info", msg: "ℹ️ Gunakan Tools Akun → Bulk Bot untuk menambah akun baru." });
    return lastRunResult;
  } catch (err) {
    isRunning = false;
    consecutiveFailures++;
    lastRunResult = { ok: false, error: err.message };
    broadcastLog({ type: "error", msg: `❌ Error tak terduga: ${err.message} (Gagal berturut-turut: ${consecutiveFailures}/3)` });
    if (consecutiveFailures >= 3) {
      broadcastLog({ type: "error", msg: "⚠️ Auto-Scheduler Creator dihentikan otomatis karena gagal 3 kali berturut-turut." });
      stopCreatorScheduler();
    }
    return lastRunResult;
  }
}

export function startCreatorScheduler(intervalMinutes = 45) {
  creatorIntervalMinutes = normalizeIntervalMinutes(intervalMinutes);
  creatorEnabled = true;
  scheduleNextCreatorRun();
  broadcastLog({ type: "info", msg: `⏰ Scheduler Pembuat Akun dimulai: setiap ${creatorIntervalMinutes} menit` });
  return creatorIntervalMinutes;
}

export function stopCreatorScheduler() {
  const wasActive = creatorEnabled || creatorTimer !== null;
  creatorEnabled = false;
  clearCreatorTimer();
  creatorNextRunAt = null;
  if (wasActive) {
    broadcastLog({ type: "info", msg: "⏹️ Scheduler Pembuat Akun dihentikan." });
  }
}

export function startReviveScheduler(intervalMinutes = 15) {
  reviveIntervalMinutes = normalizeIntervalMinutes(intervalMinutes);
  reviveEnabled = true;
  scheduleNextReviveRun();
  broadcastLog({ type: "info", msg: `⏰ Scheduler Pemulih Akun dimulai: setiap ${reviveIntervalMinutes} menit` });
  return reviveIntervalMinutes;
}

export function stopReviveScheduler() {
  const wasActive = reviveEnabled || reviveTimer !== null;
  reviveEnabled = false;
  clearReviveTimer();
  reviveNextRunAt = null;
  if (wasActive) {
    broadcastLog({ type: "info", msg: "⏹️ Scheduler Pemulih Akun dihentikan." });
  }
}

export function scheduleTestRun(delaySeconds = 15) {
  const secs = Number(delaySeconds);
  if (!Number.isFinite(secs) || secs < 5 || secs > 300) {
    throw new Error("Delay test scheduler harus antara 5 sampai 300 detik.");
  }

  clearTestTimer();
  testRunAt = new Date(Date.now() + secs * 1000).toISOString();
  broadcastLog({ type: "info", msg: `🧪 Test scheduler dijadwalkan ${secs} detik dari sekarang.` });

  testTimer = setTimeout(async () => {
    testTimer = null;
    testRunAt = null;
    broadcastLog({ type: "info", msg: "🧪 Test scheduler trigger: memeriksa pool..." });
    await triggerAutoToken(false, { source: "test" });
  }, secs * 1000);

  return { delaySeconds: secs, runAt: testRunAt };
}

export async function stopSchedulerBot() {
  broadcastLog({ type: "info", msg: "🛑 Menghentikan bot secara paksa..." });
  const res = await stopActiveBot();
  stopCreatorScheduler();
  stopReviveScheduler();
  clearTestTimer();
  isRunning = false;
  consecutiveFailures = 0;
  broadcastLog({ type: "warn", msg: "⏹️ Bot & SEMUA scheduler dimatikan total oleh admin." });
  return { ok: true, msg: "Bot & semua scheduler (Creator + Revive) dimatikan total." };
}

export function getSchedulerStatus() {
  return {
    running: isRunning,
    creatorActive: creatorEnabled,
    creatorIntervalMinutes: creatorIntervalMinutes,
    creatorNextRunAt,
    reviveActive: reviveEnabled,
    reviveIntervalMinutes: reviveIntervalMinutes,
    reviveNextRunAt,
    lastRunAt,
    lastRunResult,
    poolNeedsRefresh: poolNeedsRefresh(),
    headless: process.env.PLAYWRIGHT_HEADLESS !== "false",
    testRunAt,
    monthlyResetActive: !!resetMonthlyTimer,
    lastMonthlyReset: lastMonthlyReset,
  };
}

// ─── Monthly Pool Reset Timer ──────────────────────────────────────────────────
// Auto-reset expired accounts + credits on the 1st of each month (or first run).
// Uses persistent tracking to prevent duplicate resets on server restart.

let resetMonthlyTimer = null;
export let lastMonthlyReset = Accounts.getLastMonthlyResetMonth() || null;

function scheduleMonthlyReset() {
  if (resetMonthlyTimer) clearInterval(resetMonthlyTimer);

  // Check every 30 minutes whether we've crossed into a new month
  resetMonthlyTimer = setInterval(() => {
    const now = new Date();
    const monthKey = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;

    // Only reset once per month — check persistent value (survives restarts)
    const persisted = Accounts.getLastMonthlyResetMonth();
    if (persisted === monthKey) return;

    try {
      const result = Accounts.resetMonthlyLimits();
      lastMonthlyReset = monthKey;
      if (result.skipped) {
        console.log(`[Pool] Monthly reset skipped — ${result.reason}`);
        return;
      }
      console.log(`[Pool] Auto monthly reset — ${result.reset} accounts (${result.month})`);
      broadcastLog({
        type: "info",
        msg: `🔄 Reset bulanan otomatis: ${result.reset} akun pool di-un-expire, runs di-reset, kredit diisi ulang ${Accounts.getPoolSettings().monthlyCredits} (${result.month})`
      });
    } catch (err) {
      console.error("[Pool] Monthly reset error:", err.message);
    }
  }, 30 * 60 * 1000); // Check every 30 minutes

  // Also run immediately on startup — safe because resetMonthlyLimits() is now idempotent
  const now = new Date();
  const monthKey = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, "0")}`;
  const persistedStartup = Accounts.getLastMonthlyResetMonth();
  if (persistedStartup !== monthKey) {
    try {
      const result = Accounts.resetMonthlyLimits();
      lastMonthlyReset = monthKey;
      if (!result.skipped) {
        console.log(`[Pool] Startup monthly reset — ${result.reset} accounts (${result.month})`);
      } else {
        console.log(`[Pool] Startup monthly reset skipped — ${result.reason}`);
      }
    } catch (err) {
      console.error("[Pool] Monthly reset error:", err.message);
    }
  }
}

// Start the timer
scheduleMonthlyReset();
