// File-based JSON store for Weavy accounts.
// Each account: { id, label, token, recipeId, addedAt, lastUsedAt, runs, maxRuns, credits, expired, archived }

import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const STORE_PATH = path.join(__dirname, "..", "data", "accounts.json");
const DELETED_PATH = path.join(__dirname, "..", "data", "deleted-emails.json");

// ─── Pool Account Limits ──────────────────────────────────────────────────────
export let DEFAULT_MAX_RUNS = 3;       // Default max runs per account per month
export let DEFAULT_MONTHLY_CREDITS = 150; // Credits reset each month
export const MONTHLY_RUN_LIMIT = 15;   // Legacy compat — kept for existing flow

// ─── Blacklist email terhapus (agar tidak di-import ulang oleh sync) ──────────
function readDeletedEmails() {
  try {
    if (fs.existsSync(DELETED_PATH)) return JSON.parse(fs.readFileSync(DELETED_PATH, "utf-8"));
  } catch {}
  return [];
}

function writeDeletedEmails(list) {
  ensureDir();
  fs.writeFileSync(DELETED_PATH, JSON.stringify(list, null, 2));
}

function addDeletedEmail(email) {
  if (!email) return;
  const list = readDeletedEmails();
  if (!list.includes(email)) {
    list.push(email);
    writeDeletedEmails(list);
    console.log(`[Blacklist] Email ${email} ditambahkan ke blacklist hapus.`);
  }
}

export function isEmailBlacklisted(email) {
  return readDeletedEmails().includes(email);
}

export function getDeletedEmails() {
  return readDeletedEmails();
}

export function clearDeletedEmails() {
  writeDeletedEmails([]);
}

function ensureDir() {
  const dir = path.dirname(STORE_PATH);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
}

export function getCurrentBillingMonth(date = new Date()) {
  const d = new Date(date);
  return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}`;
}

function getBillingMonthFromValue(value) {
  if (!value) return null;
  const d = new Date(value);
  if (Number.isNaN(d.getTime())) return null;
  return getCurrentBillingMonth(d);
}

function inferLimitMonth(account, currentMonth) {
  return (
    account.archivedMonth ||
    getBillingMonthFromValue(account.archivedAt) ||
    getBillingMonthFromValue(account.lastUsedAt) ||
    getBillingMonthFromValue(account.updatedAt) ||
    currentMonth
  );
}

function normalizeAccountState(account, currentMonth = getCurrentBillingMonth()) {
  const normalized = { ...account };
  let changed = false;

  const maxRuns = typeof normalized.maxRuns === "number" && normalized.maxRuns > 0
    ? normalized.maxRuns
    : DEFAULT_MAX_RUNS;

  if (normalized.runs == null || Number.isNaN(Number(normalized.runs))) {
    normalized.runs = 0;
    changed = true;
  } else {
    const cleanRuns = Math.max(0, Math.round(Number(normalized.runs)));
    if (cleanRuns !== normalized.runs) {
      normalized.runs = cleanRuns;
      changed = true;
    }
  }

  // Normalize credits — default 150 for pool accounts
  if (normalized.credits == null || Number.isNaN(Number(normalized.credits))) {
    normalized.credits = DEFAULT_MONTHLY_CREDITS;
    changed = true;
  }

  // Auto-expire when runs >= maxRuns (pool account limit reached)
  if (!normalized.expired && !normalized.archived && (normalized.runs || 0) >= maxRuns) {
    normalized.expired = true;
    normalized.expiredAt = normalized.expiredAt || new Date().toISOString();
    normalized.expiredMonth = normalized.expiredMonth || currentMonth;
    normalized.expireReason = normalized.expireReason || "runs_limit";
    changed = true;
  }

  // Handle legacy archive-based accounts (keep backward compatibility)
  if (normalized.archived) {
    if (
      normalized.archiveReason === "monthly_limit" &&
      (normalized.runs || 0) < MONTHLY_RUN_LIMIT
    ) {
      normalized.archived = false;
      normalized.archivedAt = null;
      normalized.archivedMonth = null;
      normalized.archiveReason = null;
      normalized.restoredAt = new Date().toISOString();
      normalized.expired = false;
      changed = true;
    }

    const archivedTime = normalized.archivedAt ? new Date(normalized.archivedAt).getTime() : 0;
    const thirtyOneDaysMs = 31 * 24 * 60 * 60 * 1000;
    const isPastThirtyOneDays = (Date.now() - archivedTime) >= thirtyOneDaysMs;

    if (normalized.archived && normalized.archiveReason === "monthly_limit" && isPastThirtyOneDays) {
      normalized.archived = false;
      normalized.archivedAt = null;
      normalized.archivedMonth = null;
      normalized.archiveReason = null;
      normalized.restoredAt = new Date().toISOString();
      normalized.runs = 0;
      normalized.expired = false;
      changed = true;
    }
  }

  // Auto-un-expire if runs < maxRuns and we're in a new month
  if (normalized.expired && !normalized.archived) {
    const expiredMonth = normalized.expiredMonth || getBillingMonthFromValue(normalized.expiredAt) || "";
    if (expiredMonth !== currentMonth) {
      // New month — reset expired but keep the runs until monthly reset
      // Monthly reset will handle full reset
    }
    // If somehow runs fell below maxRuns, un-expire
    if ((normalized.runs || 0) < maxRuns) {
      normalized.expired = false;
      normalized.expiredAt = null;
      normalized.expiredMonth = null;
      normalized.expireReason = null;
      changed = true;
    }
  }

  if (!normalized.archived && normalized.archiveReason === "monthly_limit") {
    normalized.archiveReason = null;
    normalized.archivedMonth = null;
    normalized.archivedAt = null;
    changed = true;
  }

  return { account: normalized, changed };
}

function normalizeAccountList(list) {
  const currentMonth = getCurrentBillingMonth();
  let changed = false;
  const normalized = list.map((account) => {
    const result = normalizeAccountState(account, currentMonth);
    if (result.changed) changed = true;
    return result.account;
  });
  return { accounts: normalized, changed };
}

function readAll() {
  ensureDir();
  if (!fs.existsSync(STORE_PATH)) return [];
  try {
    const raw = JSON.parse(fs.readFileSync(STORE_PATH, "utf-8"));
    const { accounts, changed } = normalizeAccountList(Array.isArray(raw) ? raw : []);
    if (changed) {
      writeAll(accounts);
    }
    return accounts;
  } catch {
    return [];
  }
}

function writeAll(list) {
  ensureDir();
  fs.writeFileSync(STORE_PATH, JSON.stringify(list, null, 2));
}

export function decodeJwt(token) {
  try {
    const [, payload] = token.split(".");
    const json = Buffer.from(payload, "base64").toString("utf-8");
    return JSON.parse(json);
  } catch {
    return null;
  }
}

export function isExpired(token) {
  const d = decodeJwt(token);
  if (!d?.exp) return false;
  return Date.now() / 1000 > d.exp;
}

export function listAccounts() {
  return readAll();
}

export function listAccountsSafe({ archived = false, includeAll = false } = {}) {
  return readAll()
    .filter((a) => includeAll || Boolean(a.archived) === Boolean(archived))
    .map((a) => {
    const decoded = decodeJwt(a.token);
    const jwtExpired = isExpired(a.token);
    return {
      id: a.id,
      label: a.label,
      recipeId: a.recipeId,
      addedAt: a.addedAt,
      lastUsedAt: a.lastUsedAt,
      runs: a.runs || 0,
      maxRuns: a.maxRuns || DEFAULT_MAX_RUNS,
      credits: a.credits || DEFAULT_MONTHLY_CREDITS,
      email: decoded?.email,
      exp: decoded?.exp,
      expired: jwtExpired || Boolean(a.expired),
      expiredAt: a.expiredAt || null,
      expiredMonth: a.expiredMonth || null,
      expireReason: a.expireReason || null,
      archived: Boolean(a.archived),
      archivedAt: a.archivedAt || null,
      archivedMonth: a.archivedMonth || null,
      archiveReason: a.archiveReason || null,
      restoredAt: a.restoredAt || null,
      tokenPreview: a.token.slice(0, 24) + "..." + a.token.slice(-12),
    };
  });
}

export function listArchivedAccountsSafe() {
  return listAccountsSafe({ archived: true });
}

export function getAccount(id) {
  return readAll().find((a) => a.id === id) || null;
}

export async function pushToRemoteServer(acc) {
  const remoteUrl = process.env.REMOTE_SERVER_URL;
  const remotePass = process.env.REMOTE_ADMIN_PASSWORD || process.env.ADMIN_PASSWORD;

  if (!remoteUrl) return;

  const url = remoteUrl.replace(/\/$/, "");
  console.log(`[Sync] Mengirim akun ${acc.email} ke remote server di ${url}...`);

  try {
    if (typeof fetch === "undefined") {
      console.warn("[Sync] Global fetch tidak tersedia di versi Node ini. Gagal push.");
      return;
    }
    const res = await fetch(`${url}/api/admin/accounts`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        "x-admin-password": remotePass,
      },
      body: JSON.stringify({
        token: acc.token,
        recipeId: acc.recipeId,
        label: acc.label,
        email: acc.email,
        password: acc.password,
        runs: acc.runs || 0,
        expired: Boolean(acc.expired),
        archived: Boolean(acc.archived),
        archivedAt: acc.archivedAt || null,
        archivedMonth: acc.archivedMonth || null,
        archiveReason: acc.archiveReason || null,
        restoredAt: acc.restoredAt || null,
      }),
    });

    if (res.ok) {
      console.log(`[Sync] ✅ Akun ${acc.email} berhasil disinkronisasi ke remote server.`);
    } else {
      const data = await res.json().catch(() => ({}));
      console.error(`[Sync] ❌ Gagal sinkronisasi: ${data.error || "Unknown error"}`);
    }
  } catch (err) {
    console.error(`[Sync] ❌ Error menghubungi remote server: ${err.message}`);
  }
}

export async function deleteFromRemoteServer(email) {
  const remoteUrl = process.env.REMOTE_SERVER_URL;
  const remotePass = process.env.REMOTE_ADMIN_PASSWORD || process.env.ADMIN_PASSWORD;

  if (!remoteUrl || !email) return;

  const url = remoteUrl.replace(/\/$/, "");
  console.log(`[Sync] Menghapus akun ${email} di remote server...`);

  try {
    const res = await fetch(`${url}/api/admin/accounts/by-email?email=${encodeURIComponent(email)}`, {
      method: "DELETE",
      headers: {
        "x-admin-password": remotePass,
      },
    });
    if (res.ok) {
      console.log(`[Sync] ✅ Akun ${email} berhasil dihapus di remote server.`);
    } else {
      const data = await res.json().catch(() => ({}));
      console.error(`[Sync] ❌ Gagal menghapus di remote: ${data.error || "Unknown error"}`);
    }
  } catch (err) {
    console.error(`[Sync] ❌ Error hapus remote: ${err.message}`);
  }
}

export async function syncWithRemoteServer() {
  const remoteUrl = process.env.REMOTE_SERVER_URL;
  const remotePass = process.env.REMOTE_ADMIN_PASSWORD || process.env.ADMIN_PASSWORD;

  if (!remoteUrl) return { ok: false, error: "REMOTE_SERVER_URL not configured" };

  const url = remoteUrl.replace(/\/$/, "");
  console.log(`[Sync] Sinkronisasi data dengan remote server di ${url}...`);

  try {
    const res = await fetch(`${url}/api/admin/accounts/raw`, {
      method: "GET",
      headers: {
        "x-admin-password": remotePass,
      },
    });

    if (!res.ok) {
      const data = await res.json().catch(() => ({}));
      throw new Error(data.error || `HTTP ${res.status}`);
    }

    const { accounts: remoteAccountsRaw } = await res.json();
    const { accounts: remoteAccounts } = normalizeAccountList(Array.isArray(remoteAccountsRaw) ? remoteAccountsRaw : []);
    if (!Array.isArray(remoteAccounts)) {
      throw new Error("Invalid response format from remote server");
    }

    const localAccounts = readAll();
    let updated = false;

    // Load blacklist email yang sudah dihapus
    const deletedEmails = readDeletedEmails();

    // 1. Merge remote accounts into local database
    for (const rAcc of remoteAccounts) {
      // Skip akun yang ada di blacklist hapus
      if (rAcc.email && deletedEmails.includes(rAcc.email)) {
        console.log(`[Sync] ⛔ Skip akun ${rAcc.email} — ada di blacklist hapus.`);
        continue;
      }

      const lIdx = localAccounts.findIndex(a => a.email === rAcc.email && rAcc.email !== "");
      if (lIdx < 0) {
        // Akun baru dari remote, masukkan ke lokal
        localAccounts.push({
          id: rAcc.id || "acc_" + Math.random().toString(36).slice(2, 12),
          label: rAcc.label,
          token: rAcc.token,
          recipeId: rAcc.recipeId,
          email: rAcc.email,
          password: rAcc.password,
          addedAt: rAcc.addedAt || new Date().toISOString(),
          lastUsedAt: rAcc.lastUsedAt || null,
          runs: rAcc.runs || 0,
          expired: Boolean(rAcc.expired),
          archived: Boolean(rAcc.archived),
          archivedAt: rAcc.archivedAt || null,
          archivedMonth: rAcc.archivedMonth || null,
          archiveReason: rAcc.archiveReason || null,
          restoredAt: rAcc.restoredAt || null,
        });
        updated = true;
        console.log(`[Sync] Akun baru diunduh: ${rAcc.email}`);
      } else {
        // Akun sudah ada di lokal, update jika remote lebih baru atau tokennya beda
        const lAcc = localAccounts[lIdx];
        const maxRuns = Math.max(lAcc.runs || 0, rAcc.runs || 0);
        const mergedArchived = Boolean(lAcc.archived || rAcc.archived);
        const mergedArchiveMonth = mergedArchived ? (lAcc.archivedMonth || rAcc.archivedMonth || getCurrentBillingMonth()) : null;
        const mergedArchiveReason = mergedArchived ? (lAcc.archiveReason || rAcc.archiveReason || "monthly_limit") : null;
        const mergedArchivedAt = mergedArchived ? (lAcc.archivedAt || rAcc.archivedAt || new Date().toISOString()) : null;
        const mergedRestoredAt = !mergedArchived ? (lAcc.restoredAt || rAcc.restoredAt || null) : null;
        const needsLocalUpdate =
          lAcc.token !== rAcc.token ||
          lAcc.expired !== rAcc.expired ||
          lAcc.runs !== maxRuns ||
          Boolean(lAcc.archived) !== mergedArchived ||
          (lAcc.archivedMonth || null) !== mergedArchiveMonth ||
          (lAcc.archiveReason || null) !== mergedArchiveReason;
        const needsRemoteUpdate =
          rAcc.runs < maxRuns ||
          (!rAcc.expired && lAcc.expired) ||
          Boolean(rAcc.archived) !== mergedArchived ||
          (rAcc.archivedMonth || null) !== mergedArchiveMonth ||
          (rAcc.archiveReason || null) !== mergedArchiveReason;

        if (needsLocalUpdate) {
          lAcc.token = rAcc.token;
          lAcc.recipeId = rAcc.recipeId || lAcc.recipeId;
          lAcc.password = rAcc.password || lAcc.password;
          lAcc.label = rAcc.label || lAcc.label;
          lAcc.expired = Boolean(rAcc.expired);
          lAcc.runs = maxRuns;
          lAcc.lastUsedAt = rAcc.lastUsedAt || lAcc.lastUsedAt;
          lAcc.archived = mergedArchived;
          lAcc.archivedAt = mergedArchivedAt;
          lAcc.archivedMonth = mergedArchiveMonth;
          lAcc.archiveReason = mergedArchiveReason;
          lAcc.restoredAt = mergedRestoredAt;
          updated = true;
          console.log(`[Sync] Mengupdate akun lokal: ${rAcc.email} (runs: ${maxRuns})`);
        }

        if (needsRemoteUpdate) {
          console.log(`[Sync] Remote memiliki data runs lama (${rAcc.runs}) dibanding lokal (${maxRuns}) untuk ${lAcc.email}. Mengupdate remote...`);
          pushToRemoteServer(lAcc).catch(() => {});
        }
      }
    }

    // 2. Upload local accounts that are missing on remote
    for (const lAcc of localAccounts) {
      const rIdx = remoteAccounts.findIndex(a => a.email === lAcc.email && lAcc.email !== "");
      const lMaxRuns = lAcc.maxRuns || DEFAULT_MAX_RUNS;
      if (rIdx < 0 && lAcc.email && !lAcc.expired && !lAcc.archived && (lAcc.runs || 0) < lMaxRuns && !isExpired(lAcc.token)) {
        console.log(`[Sync] Akun lokal ${lAcc.email} tidak ada di remote. Mengupload...`);
        await pushToRemoteServer(lAcc).catch(() => {});
      }
    }

    if (updated) {
      writeAll(localAccounts);
      console.log("[Sync] ✅ Sinkronisasi lokal database selesai.");
    } else {
      console.log("[Sync] ✅ Semua data sudah sinkron.");
    }

    return { ok: true, msg: "Synchronization successful" };
  } catch (err) {
    console.error(`[Sync] ❌ Error sinkronisasi: ${err.message}`);
    return { ok: false, error: err.message };
  }
}

export function addAccount({
  token,
  recipeId = "",
  label = "",
  email = "",
  password = "",
  runs = undefined,
  maxRuns = undefined,
  credits = undefined,
  expired = undefined,
  archived = undefined,
  archivedAt = undefined,
  archivedMonth = undefined,
  archiveReason = undefined,
  restoredAt = undefined,
}) {
  const decoded = decodeJwt(token);
  if (!decoded) throw new Error("Invalid JWT token");

  const list = readAll();
  const targetEmail = email || decoded.email || "";
  
  // Check if account already exists by email
  const existingIdx = list.findIndex(a => a.email === targetEmail && targetEmail !== "");

  if (existingIdx >= 0) {
    const acc = list[existingIdx];
    if (acc.token !== token) {
      acc.token = token;
      acc.expired = expired !== undefined ? expired : false;
      acc.runs = runs !== undefined ? runs : (acc.runs || 0);
      acc.lastUsedAt = acc.lastUsedAt || null;
    } else {
      if (runs !== undefined) acc.runs = runs;
      if (expired !== undefined) acc.expired = expired;
    }
    if (recipeId) acc.recipeId = recipeId;
    if (password) acc.password = password;
    if (label) acc.label = label;
    if (maxRuns !== undefined) acc.maxRuns = maxRuns;
    if (credits !== undefined) acc.credits = credits;
    if (archived !== undefined) acc.archived = archived;
    if (archivedAt !== undefined) acc.archivedAt = archivedAt;
    if (archivedMonth !== undefined) acc.archivedMonth = archivedMonth;
    if (archiveReason !== undefined) acc.archiveReason = archiveReason;
    if (restoredAt !== undefined) acc.restoredAt = restoredAt;
    const normalized = normalizeAccountState(acc).account;
    list[existingIdx] = normalized;
    writeAll(list);
    
    // Sync to remote
    pushToRemoteServer(normalized).catch((err) => {
      console.error("[Sync] Gagal mengirim update ke remote server:", err.message);
    });
    
    return normalized;
  }

  const id = "acc_" + Math.random().toString(36).slice(2, 12);
  const acc = {
    id,
    label: label || decoded.email || decoded.name || "Akun",
    token,
    recipeId,
    email: targetEmail,
    password: password || "",
    addedAt: new Date().toISOString(),
    lastUsedAt: null,
    runs: runs !== undefined ? runs : 0,
    maxRuns: maxRuns !== undefined ? maxRuns : DEFAULT_MAX_RUNS,
    credits: credits !== undefined ? credits : DEFAULT_MONTHLY_CREDITS,
    expired: expired !== undefined ? expired : false,
    archived: archived !== undefined ? archived : false,
    archivedAt: archivedAt !== undefined ? archivedAt : null,
    archivedMonth: archivedMonth !== undefined ? archivedMonth : null,
    archiveReason: archiveReason !== undefined ? archiveReason : null,
    restoredAt: restoredAt !== undefined ? restoredAt : null,
  };
  const normalized = normalizeAccountState(acc).account;
  list.push(normalized);
  writeAll(list);

  // Sync to remote
  pushToRemoteServer(normalized).catch((err) => {
    console.error("[Sync] Gagal mengirim akun ke remote server:", err.message);
  });

  return normalized;
}

export function updateAccount(id, patch) {
  const list = readAll();
  const idx = list.findIndex((a) => a.id === id);
  if (idx < 0) throw new Error("Account not found");
  if (patch.token) {
    const decoded = decodeJwt(patch.token);
    if (!decoded) throw new Error("Invalid JWT token");
  }
  list[idx] = normalizeAccountState({ ...list[idx], ...patch }).account;
  writeAll(list);

  // Sync to remote for all updates (such as expired status or runs)
  pushToRemoteServer(list[idx]).catch((err) => {
    console.error("[Sync] Gagal mengirim update ke remote server:", err.message);
  });

  return list[idx];
}


export function deleteAccount(id) {
  const list = readAll();
  const idx = list.findIndex((a) => a.id === id);
  if (idx < 0) return false;
  const acc = list[idx];
  list.splice(idx, 1);
  writeAll(list);
  
  // Tambahkan ke blacklist agar tidak di-import ulang oleh sync
  if (acc.email) {
    addDeletedEmail(acc.email);
    deleteFromRemoteServer(acc.email).catch((err) => {
      console.error("[Sync] Gagal mengirim delete ke remote server:", err.message);
    });
  }
  return true;
}

export function deleteAccounts(ids) {
  const list = readAll();
  const deletedEmailsList = [];
  const remaining = list.filter((a) => {
    if (ids.includes(a.id)) {
      if (a.email) deletedEmailsList.push(a.email);
      return false;
    }
    return true;
  });
  
  if (list.length === remaining.length) return 0;
  
  writeAll(remaining);
  
  // Tambahkan ke blacklist dan sync hapus ke remote
  for (const email of deletedEmailsList) {
    addDeletedEmail(email);
    deleteFromRemoteServer(email).catch((err) => {
      console.error("[Sync] Gagal mengirim delete ke remote server:", err.message);
    });
  }
  
  return deletedEmailsList.length;
}

export function archiveAccounts(ids, { reason = "manual" } = {}) {
  const idSet = new Set(Array.isArray(ids) ? ids : []);
  if (!idSet.size) return 0;

  const list = readAll();
  const currentMonth = getCurrentBillingMonth();
  const archivedAt = new Date().toISOString();
  const changedAccounts = [];

  for (let i = 0; i < list.length; i++) {
    const acc = list[i];
    if (!idSet.has(acc.id) || acc.archived) continue;
    list[i] = normalizeAccountState({
      ...acc,
      archived: true,
      archivedAt,
      archivedMonth: currentMonth,
      archiveReason: reason || "manual",
      expired: false,
    }).account;
    changedAccounts.push(list[i]);
  }

  if (!changedAccounts.length) return 0;

  writeAll(list);

  for (const acc of changedAccounts) {
    pushToRemoteServer(acc).catch((err) => {
      console.error("[Sync] Gagal mengirim archive akun ke remote server:", err.message);
    });
  }

  return changedAccounts.length;
}

export function deleteAccountByEmail(email) {
  const list = readAll();
  const idx = list.findIndex((a) => a.email === email);
  if (idx < 0) return false;
  list.splice(idx, 1);
  writeAll(list);
  return true;
}

export function markUsed(id) {
  const list = readAll();
  const idx = list.findIndex((a) => a.id === id);
  if (idx < 0) return;
  list[idx].lastUsedAt = new Date().toISOString();
  list[idx].runs = (list[idx].runs || 0) + 1;
  list[idx] = normalizeAccountState(list[idx]).account;
  writeAll(list);

  // Immediately sync runs increment to remote server
  pushToRemoteServer(list[idx]).catch((err) => {
    console.error("[Sync] Gagal mengirim update runs ke remote server:", err.message);
  });
}

// ─── Pool Account Settings ─────────────────────────────────────────────────────
// Settable from admin dashboard, applies to all pool accounts.

export function getPoolSettings() {
  return {
    defaultMaxRuns: DEFAULT_MAX_RUNS,
    monthlyCredits: DEFAULT_MONTHLY_CREDITS,
  };
}

export function updatePoolSettings({ maxRuns, monthlyCredits }) {
  if (typeof maxRuns === "number" && maxRuns > 0) {
    DEFAULT_MAX_RUNS = maxRuns;
  }
  if (typeof monthlyCredits === "number" && monthlyCredits > 0) {
    DEFAULT_MONTHLY_CREDITS = monthlyCredits;
  }
}

// Per-account max runs override.  null / 0  → fall back to DEFAULT_MAX_RUNS.
export function setAccountMaxRuns(id, maxRuns) {
  const list = readAll();
  const idx = list.findIndex((a) => a.id === id);
  if (idx < 0) return null;
  list[idx].maxRuns = maxRuns > 0 ? maxRuns : undefined;
  list[idx] = normalizeAccountState(list[idx]).account;
  writeAll(list);
  return list[idx];
}

// ─── Monthly Reset ──────────────────────────────────────────────────────────────
// Resets all pool accounts: un-expire, runs → 0, credits → DEFAULT_MONTHLY_CREDITS.

export function resetMonthlyLimits() {
  const list = readAll();
  const currentMonth = getCurrentBillingMonth();
  let changed = false;

  for (let i = 0; i < list.length; i++) {
    const a = list[i];
    if (a.archived) continue; // jangan reset akun archived

    // Reset expired state
    if (a.expired) {
      a.expired = false;
      a.expiredAt = null;
      a.expiredMonth = null;
      a.expireReason = null;
      changed = true;
    }

    // Reset runs
    if (a.runs !== 0) {
      a.runs = 0;
      changed = true;
    }

    // Reset credits
    if ((a.credits || DEFAULT_MONTHLY_CREDITS) !== DEFAULT_MONTHLY_CREDITS) {
      a.credits = DEFAULT_MONTHLY_CREDITS;
      changed = true;
    }
  }

  if (changed) {
    writeAll(list);
  }

  return {
    total: list.length,
    active: list.filter((a) => !a.archived).length,
    reset: list.filter((a) => !a.archived).length,
    month: currentMonth,
  };
}

// Pick the next available (non-expired) account using least-recently-used.
// excludeIds: Set of account IDs to skip (for retry logic)
// preferredId: try this ID first (legacy support)
export function pickAccount(excludeIds = null, { preferredId } = {}) {
  const excluded = excludeIds instanceof Set ? excludeIds : new Set();
  const list = readAll().filter((a) => {
    const maxRuns = typeof a.maxRuns === "number" && a.maxRuns > 0 ? a.maxRuns : DEFAULT_MAX_RUNS;
    return (
      !isExpired(a.token) &&    // JWT not expired
      !a.expired &&             // not marked expired (runs limit or manual)
      !a.archived &&            // akun archived tidak boleh dipakai di bulan ini
      (a.runs || 0) < maxRuns && // masih ada sisa runs bulan ini
      !excluded.has(a.id)       // not in excluded set
    );
  });
  if (!list.length) return null;
  if (preferredId) {
    const p = list.find((a) => a.id === preferredId);
    if (p) return p;
  }
  // Sort by runs descending (prioritize accounts closest to the monthly limit)
  list.sort((a, b) => {
    const runsA = a.runs || 0;
    const runsB = b.runs || 0;
    if (runsA !== runsB) {
      return runsB - runsA;
    }
    // Fallback to LRU: never used → first; else oldest lastUsedAt
    if (!a.lastUsedAt && b.lastUsedAt) return -1;
    if (a.lastUsedAt && !b.lastUsedAt) return 1;
    if (!a.lastUsedAt && !b.lastUsedAt) return 0;
    return new Date(a.lastUsedAt) - new Date(b.lastUsedAt);
  });
  return list[0];
}

export function deleteExpiredAccounts() {
  const list = readAll();
  const remaining = [];
  const deletedEmailsList = [];
  const deletedIds = [];

  for (const a of list) {
    const jwtExpired = isExpired(a.token);
    const manuallyExpired = Boolean(a.expired);
    if (jwtExpired || manuallyExpired) {
      if (a.email) deletedEmailsList.push(a.email);
      deletedIds.push(a.id);
    } else {
      remaining.push(a);
    }
  }

  if (deletedIds.length === 0) return 0;

  writeAll(remaining);

  // Tambahkan ke blacklist dan sync hapus ke remote
  for (const email of deletedEmailsList) {
    addDeletedEmail(email);
    deleteFromRemoteServer(email).catch((err) => {
      console.error("[Sync] Gagal mengirim delete ke remote server:", err.message);
    });
  }

  return deletedIds.length;
}

export function restoreArchivedAccount(id, { force = false } = {}) {
  const list = readAll();
  const idx = list.findIndex((a) => a.id === id);
  if (idx < 0) throw new Error("Account not found");

  const acc = list[idx];
  if (!acc.archived) {
    return acc;
  }

  const currentMonth = getCurrentBillingMonth();
  if (!force && acc.archivedMonth === currentMonth) {
    throw new Error("Akun archive ini masih berada di bulan kredit yang sama.");
  }

  list[idx] = normalizeAccountState({
    ...acc,
    archived: false,
    archivedAt: null,
    archivedMonth: null,
    archiveReason: null,
    restoredAt: new Date().toISOString(),
    runs: 0,
    expired: false,
  }).account;
  writeAll(list);

  pushToRemoteServer(list[idx]).catch((err) => {
    console.error("[Sync] Gagal mengirim restore akun ke remote server:", err.message);
  });

  return list[idx];
}

export function deleteAllArchivedAccounts() {
  const list = readAll();
  const deletedEmailsList = [];
  const remaining = [];

  for (const a of list) {
    if (a.archived) {
      if (a.email) deletedEmailsList.push(a.email);
    } else {
      remaining.push(a);
    }
  }

  const deletedCount = deletedEmailsList.length;
  if (deletedCount === 0) return 0;

  writeAll(remaining);

  // Blacklist and sync delete to remote
  for (const email of deletedEmailsList) {
    addDeletedEmail(email);
    deleteFromRemoteServer(email).catch((err) => {
      console.error("[Sync] Gagal mengirim delete ke remote server:", err.message);
    });
  }

  return deletedCount;
}

export function deleteAllActiveAccounts() {
  const list = readAll();
  const deletedEmailsList = [];
  const remaining = [];

  for (const a of list) {
    if (!a.archived) {
      if (a.email) deletedEmailsList.push(a.email);
    } else {
      remaining.push(a);
    }
  }

  const deletedCount = deletedEmailsList.length;
  if (deletedCount === 0) return 0;

  writeAll(remaining);

  // Blacklist and sync delete to remote
  for (const email of deletedEmailsList) {
    addDeletedEmail(email);
    deleteFromRemoteServer(email).catch((err) => {
      console.error("[Sync] Gagal mengirim delete ke remote server:", err.message);
    });
  }

  return deletedCount;
}

