const express = require("express");
const crypto = require("crypto");
const fetch = require("node-fetch");
const http = require("http");
const https = require("https");
const { URL } = require("url");
const { HttpsProxyAgent } = require("https-proxy-agent");
const Database = require("better-sqlite3");
const path = require("path");
const config = require("./config");

// ==================== PROXY MANAGER ====================
let activeProxy = null;      // { host, port } yang sedang dipakai
let freeProxyList = [];      // daftar proxy gratis
let proxyFetchedAt = 0;      // kapan terakhir fetch
let proxyTesting = false;

function buildProxyAgent(host, port) {
  try {
    return new HttpsProxyAgent(`http://${host}:${port}`);
  } catch (e) {
    return null;
  }
}

// Cek apakah proxy bisa forward request HTTPS nyata
async function testProxy(host, port) {
  return new Promise((resolve) => {
    // Step 1: buka tunnel CONNECT
    const connectReq = http.request({
      host, port,
      method:  "CONNECT",
      path:    "app.orderkuota.com:443",
      headers: { Host: "app.orderkuota.com:443" },
    });
    connectReq.setTimeout(6000, () => { connectReq.destroy(); resolve(false); });
    connectReq.on("error", () => resolve(false));
    connectReq.on("connect", (_res, socket) => {
      // Step 2: buka koneksi TLS melalui tunnel
      const tlsSock = require("tls").connect({
        socket,
        host: "app.orderkuota.com",
        servername: "app.orderkuota.com",
        rejectUnauthorized: false,
      });
      tlsSock.setTimeout(6000, () => { tlsSock.destroy(); resolve(false); });
      tlsSock.on("error", () => resolve(false));
      tlsSock.on("secureConnect", () => {
        // Step 3: kirim HTTP HEAD request
        tlsSock.write("HEAD /api/v2 HTTP/1.1\r\nHost: app.orderkuota.com\r\nConnection: close\r\n\r\n");
        tlsSock.once("data", () => { tlsSock.destroy(); resolve(true); });
        tlsSock.setTimeout(5000, () => { tlsSock.destroy(); resolve(false); });
      });
    });
    connectReq.end();
  });
}

// Ambil daftar proxy gratis dari beberapa sumber
async function fetchFreeProxies() {
  const sources = [
    "https://proxylist.geonode.com/api/proxy-list?limit=30&page=1&sort_by=lastChecked&sort_type=desc&protocols=http%2Chttps&country=ID",
    "https://proxylist.geonode.com/api/proxy-list?limit=30&page=1&sort_by=lastChecked&sort_type=desc&protocols=http%2Chttps",
  ];
  let proxies = [];
  for (const src of sources) {
    try {
      const r = await fetch(src, { timeout: 10000 });
      const j = await r.json();
      if (Array.isArray(j?.data)) {
        proxies.push(...j.data.map(p => ({ host: p.ip, port: parseInt(p.port) })));
      }
    } catch (_) {}
    if (proxies.length >= 20) break;
  }
  return proxies;
}

// Cari proxy yang benar-benar bisa dipakai (test paralel, lebih cepat)
async function findWorkingProxy() {
  if (proxyTesting) return;
  proxyTesting = true;
  try {
    const now = Date.now();
    if (now - proxyFetchedAt > 10 * 60 * 1000 || freeProxyList.length === 0) {
      console.log("[PROXY] Mengambil daftar proxy gratis...");
      freeProxyList = await fetchFreeProxies();
      proxyFetchedAt = now;
      console.log(`[PROXY] Dapat ${freeProxyList.length} kandidat, test paralel...`);
    }

    // Test semua sekaligus, ambil yang pertama berhasil
    let testedCount = 0;
    let failCount = 0;
    const total = freeProxyList.length;
    const winner = await Promise.any(
      freeProxyList.map(p =>
        testProxy(p.host, p.port).then(ok => {
          testedCount++;
          if (!ok) {
            failCount++;
            console.log(`[PROXY] ✗ ${p.host}:${p.port} — gagal (${testedCount}/${total})`);
            throw new Error("fail");
          }
          console.log(`[PROXY] ✓ DITEMUKAN: ${p.host}:${p.port} (${testedCount}/${total} ditest, ${failCount} gagal)`);
          return p;
        })
      )
    ).catch(() => null);

    if (winner) {
      activeProxy = winner;
      console.log(`[PROXY] ✓ Siap pakai: ${winner.host}:${winner.port}`);
    } else {
      activeProxy = null;
      console.log(`[PROXY] ✗ Semua ${total} proxy gagal — fallback koneksi langsung.`);
    }
  } finally {
    proxyTesting = false;
  }
}

function getProxyAgent() {
  // Cek dulu apakah ada proxy manual di env (prioritas utama)
  let proxyStr = (
    process.env.HTTPS_PROXY ||
    process.env.https_proxy ||
    process.env.HTTP_PROXY  ||
    process.env.http_proxy  || ""
  ).trim();

  // Abaikan kalau bukan URL valid (misal teks biasa)
  try {
    if (proxyStr) {
      if (!/^https?:\/\//i.test(proxyStr)) proxyStr = "http://" + proxyStr;
      new URL(proxyStr); // validasi
      const agent = new HttpsProxyAgent(proxyStr);
      console.log(`[PROXY] Manual: ${proxyStr.split("@").pop()}`);
      return agent;
    }
  } catch (_) {}

  // Gunakan proxy gratis yang sudah ditemukan
  if (activeProxy) {
    return buildProxyAgent(activeProxy.host, activeProxy.port);
  }
  return undefined;
}

const app = express();
const db = new Database("mutasi.db");

// ==================== DATABASE SETUP ====================
db.exec(`
  CREATE TABLE IF NOT EXISTS mutasi (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    waktu TEXT NOT NULL,
    waktu_ts INTEGER NOT NULL,
    saldo_sebelum INTEGER NOT NULL,
    saldo_sesudah INTEGER NOT NULL,
    selisih INTEGER NOT NULL,
    jenis TEXT NOT NULL,
    transaction_id TEXT,
    keterangan TEXT,
    brand_name TEXT,
    brand_logo TEXT,
    sumber TEXT NOT NULL DEFAULT 'balance_diff'
  );

  CREATE TABLE IF NOT EXISTS polling_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    waktu TEXT NOT NULL,
    waktu_ts INTEGER NOT NULL,
    saldo INTEGER NOT NULL,
    status TEXT NOT NULL
  );

  CREATE TABLE IF NOT EXISTS settings (
    key TEXT PRIMARY KEY,
    value TEXT
  );
`);

try {
  db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_diff_dedup ON mutasi(waktu_ts, selisih, jenis) WHERE sumber = 'balance_diff'");
} catch {}

try {
  db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_transaction_id ON mutasi(transaction_id) WHERE transaction_id IS NOT NULL");
} catch {}

(function migrateDB() {
  const cols = db.pragma("table_info(mutasi)").map((c) => c.name);
  if (!cols.includes("transaction_id")) {
    db.exec("ALTER TABLE mutasi ADD COLUMN transaction_id TEXT");
    console.log("[DB] Migrasi: tambah kolom transaction_id");
  }
  if (!cols.includes("keterangan")) {
    db.exec("ALTER TABLE mutasi ADD COLUMN keterangan TEXT");
    console.log("[DB] Migrasi: tambah kolom keterangan");
  }
  if (!cols.includes("brand_name")) {
    db.exec("ALTER TABLE mutasi ADD COLUMN brand_name TEXT");
    console.log("[DB] Migrasi: tambah kolom brand_name");
  }
  if (!cols.includes("brand_logo")) {
    db.exec("ALTER TABLE mutasi ADD COLUMN brand_logo TEXT");
    console.log("[DB] Migrasi: tambah kolom brand_logo");
  }
  if (!cols.includes("sumber")) {
    db.exec("ALTER TABLE mutasi ADD COLUMN sumber TEXT NOT NULL DEFAULT 'balance_diff'");
    console.log("[DB] Migrasi: tambah kolom sumber");
  }

  const deleted = db.prepare(`
    DELETE FROM mutasi
    WHERE sumber = 'balance_diff'
      AND EXISTS (
        SELECT 1 FROM mutasi q
        WHERE q.sumber = 'qris_history'
          AND q.selisih = mutasi.selisih
          AND q.jenis   = mutasi.jenis
          AND DATE(q.waktu_ts / 1000, 'unixepoch', '+7 hours') =
              DATE(mutasi.waktu_ts / 1000, 'unixepoch', '+7 hours')
      )
  `).run();
  if (deleted.changes > 0) {
    console.log(`[CLEANUP] Hapus ${deleted.changes} entri balance_diff duplikat saat startup`);
  }
})();

// ==================== STATE ====================
const swapLastBalance = db.transaction((newBalance) => {
  const row = db.prepare("SELECT value FROM settings WHERE key = 'last_balance'").get();
  const prev = row ? parseInt(row.value, 10) : null;
  db.prepare("INSERT OR REPLACE INTO settings (key, value) VALUES ('last_balance', ?)").run(String(newBalance));
  return prev;
});

let isFastMode = false;
let fastModeUntil = 0;
let pollingTimer = null;
let pollingStarted = false;
let lastRawResponse = null;
let pollingStatus = {
  aktif: true,
  mode: "normal",
  interval_detik: config.normal_interval_sec,
  saldo_terakhir: null,
  saldo_terakhir_str: null,
  cek_terakhir: null,
  fast_mode_selesai: null,
  total_mutasi: 0,
  sumber_data: "menunggu",
};

// ==================== HELPERS ====================
function formatRupiah(angka) {
  return new Intl.NumberFormat("id-ID").format(angka);
}

function nowISO() {
  return new Date().toLocaleString("id-ID", {
    timeZone: "Asia/Jakarta",
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
    second: "2-digit",
  });
}

function parseNominal(str) {
  if (!str || str === "0") return 0;
  return parseInt(String(str).replace(/\./g, ""), 10) || 0;
}

function parseTanggal(str) {
  try {
    const [tgl, jam] = str.split(" ");
    const [d, m, y] = tgl.split("/");
    return new Date(`${y}-${m}-${d}T${jam}+07:00`).getTime();
  } catch {
    return Date.now();
  }
}

// ==================== API CALL ====================
// agent: HttpsProxyAgent → pakai proxy, null/undefined → direct
async function fetchAPI(agent) {
  const timestamp = Date.now();
  const signature = crypto
    .createHash("sha512")
    .update(timestamp + config.auth_token + config.username)
    .digest("hex");

  const token = config.auth_token.split(":")[0];
  const url = `https://app.orderkuota.com/api/v2/qris/mutasi/${token}`;

  const params = new URLSearchParams({
    app_reg_id: config.app_reg_id,
    phone_uuid: config.phone_uuid,
    phone_model: config.phone_model,
    "requests[qris_history][keterangan]": "",
    "requests[qris_history][jumlah]": "",
    request_time: timestamp,
    phone_android_version: config.phone_android_version,
    app_version_code: config.app_version_code,
    auth_username: config.username,
    "requests[qris_history][page]": "1",
    auth_token: config.auth_token,
    app_version_name: config.app_version_name,
    ui_mode: config.ui_mode,
    "requests[qris_history][dari_tanggal]": "",
    "requests[0]": "account",
    "requests[qris_history][ke_tanggal]": "",
  });

  const fetchOpts = {
    method: "POST",
    headers: {
      "User-Agent": "okhttp/4.12.0",
      "Content-Type": "application/x-www-form-urlencoded",
      signature: signature,
      timestamp: String(timestamp),
    },
    body: params.toString(),
    timeout: 15000,
  };
  if (agent) fetchOpts.agent = agent;

  const res = await fetch(url, fetchOpts);

  const text = await res.text();

  let json;
  try {
    json = JSON.parse(text);
  } catch (e) {
    console.error(`[PARSE ERROR] Bukan JSON. Status: ${res.status}. Isi: ${text.substring(0, 200)}`);
    return null;
  }

  if (json?.success === false || json?.status === false) {
    console.error(`[API ERROR] ${json?.message || JSON.stringify(json).substring(0, 200)}`);
    return null;
  }

  const balance = json?.account?.results?.qris_balance;
  if (typeof balance !== "number") {
    console.error(`[PARSE ERROR] qris_balance tidak ditemukan. Keys: ${Object.keys(json || {}).join(", ")}`);
    return null;
  }

  lastRawResponse = {
    diambil_pada: nowISO(),
    status: "success",
    http_code: res.status,
    data: json,
  };

  return {
    balance,
    qris_history: json?.qris_history || null,
  };
}

// ==================== SIMPAN DARI QRIS HISTORY ====================
function simpanDariQrisHistory(results, currentBalance) {
  let ada_baru = false;

  const insertStmt = db.prepare(`
    INSERT OR IGNORE INTO mutasi
      (waktu, waktu_ts, saldo_sebelum, saldo_sesudah, selisih, jenis,
       transaction_id, keterangan, brand_name, brand_logo, sumber)
    VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'qris_history')
  `);

  try {
    db.exec("CREATE UNIQUE INDEX IF NOT EXISTS idx_transaction_id ON mutasi(transaction_id) WHERE transaction_id IS NOT NULL");
  } catch {}

  for (const tx of results) {
    const txId = String(tx.id);
    const existing = db.prepare("SELECT id FROM mutasi WHERE transaction_id = ?").get(txId);
    if (existing) continue;

    const selisih = parseNominal(tx.kredit) || parseNominal(tx.debet);
    const jenis = tx.status === "IN" ? "masuk" : "keluar";
    const saldoSesudah = parseNominal(tx.saldo_akhir);
    const saldoSebelum = jenis === "masuk"
      ? saldoSesudah - selisih
      : saldoSesudah + selisih;
    const ts = parseTanggal(tx.tanggal);

    const duplikat = db.prepare(`
      SELECT id FROM mutasi
      WHERE sumber = 'balance_diff'
        AND selisih = ?
        AND jenis = ?
        AND waktu_ts >= ?
        AND waktu_ts <= ?
    `).all(selisih, jenis, ts, ts + 24 * 60 * 60 * 1000);

    if (duplikat.length > 0) {
      const ids = duplikat.map((r) => r.id).join(",");
      db.exec(`DELETE FROM mutasi WHERE id IN (${ids})`);
      console.log(`[DEDUP] Hapus ${duplikat.length} entri balance_diff duplikat untuk tx ${txId} (${formatRupiah(selisih)})`);
    }

    insertStmt.run(
      tx.tanggal, ts, saldoSebelum, saldoSesudah, selisih, jenis,
      txId, tx.keterangan || "", tx.brand?.name || "", tx.brand?.logo || ""
    );

    console.log(`[QRIS HISTORY] ${tx.tanggal} | ${jenis.toUpperCase()} | ${formatRupiah(selisih)} | ${tx.keterangan} (${tx.brand?.name})`);
    ada_baru = true;
  }

  return ada_baru;
}

// ==================== POLLING LOGIC ====================
async function doPoll() {
  try {
    await doPollInner();
  } catch (err) {
    console.error(`[POLL CRASH] ${err.message}`);
    pollingStatus.cek_terakhir = nowISO();
  } finally {
    scheduleNext();
  }
}

async function doPollInner() {
  const now = Date.now();
  const mode = config.proxy_mode || "proxy";

  let apiResult = null;

  if (mode === "direct") {
    // ── Tanpa proxy ──
    console.log("[POLL] Mulai cek langsung (tanpa proxy)...");
    try {
      apiResult = await fetchAPI(null);
      if (apiResult) console.log("[POLL] ✓ API berhasil diakses langsung (tanpa proxy)");
    } catch (err) {
      console.error(`[POLL ERROR] ${err.message}`);
      pollingStatus.cek_terakhir = nowISO();
      return;
    }

  } else if (mode === "proxy") {
    // ── Pakai proxy, langsung rotasi kalau 1x gagal ──
    const pa = getProxyAgent();
    const paLabel = activeProxy ? `${activeProxy.host}:${activeProxy.port}` : "tidak ada";
    console.log(`[POLL] Mulai cek via proxy ${paLabel}...`);
    try {
      apiResult = await fetchAPI(pa);
      console.log(`[POLL] ✓ API berhasil diakses via proxy ${paLabel}`);
    } catch (err) {
      console.error(`[POLL ERROR] ${err.message}`);
      pollingStatus.cek_terakhir = nowISO();
      console.log(`[PROXY] Proxy ${paLabel} gagal 1x → cari proxy baru...`);
      activeProxy = null;
      findWorkingProxy().catch(() => {});
      return;
    }

  } else {
    // ── Auto: coba direct dulu, gagal → proxy ──
    console.log("[POLL] Mulai cek (mode: auto, coba direct dulu)...");
    try {
      apiResult = await fetchAPI(null);
      if (apiResult) console.log("[POLL] ✓ API berhasil diakses langsung (tanpa proxy)");
    } catch (directErr) {
      console.log(`[AUTO] Direct gagal (${directErr.message.substring(0,60)}), coba proxy...`);
      const pa2 = getProxyAgent();
      const pa2Label = activeProxy ? `${activeProxy.host}:${activeProxy.port}` : "tidak ada";
      try {
        apiResult = await fetchAPI(pa2);
        console.log(`[POLL] ✓ API berhasil diakses via proxy ${pa2Label}`);
      } catch (proxyErr) {
        console.error(`[POLL ERROR] Direct+Proxy gagal: ${proxyErr.message}`);
        pollingStatus.cek_terakhir = nowISO();
        activeProxy = null;
        findWorkingProxy().catch(() => {});
        return;
      }
    }
  }

  const waktuStr = nowISO();
  pollingStatus.cek_terakhir = waktuStr;

  if (!apiResult) {
    console.log(`[POLL] ${waktuStr} - Gagal ambil data`);
    return;
  }

  const { balance: newBalance, qris_history } = apiResult;

  pollingStatus.saldo_terakhir = newBalance;
  pollingStatus.saldo_terakhir_str = formatRupiah(newBalance);

  db.prepare(
    "INSERT INTO polling_log (waktu, waktu_ts, saldo, status) VALUES (?, ?, ?, ?)"
  ).run(waktuStr, now, newBalance, "ok");

  // ===== MODE 1: qris_history tersedia =====
  if (qris_history?.success === true && Array.isArray(qris_history.results) && qris_history.results.length > 0) {
    pollingStatus.sumber_data = "qris_history";

    // FIX: selalu sync last_balance agar MODE 2 tidak double-count
    swapLastBalance(newBalance);

    const ada_baru = simpanDariQrisHistory(qris_history.results, newBalance);

    if (ada_baru) {
      pollingStatus.total_mutasi = db.prepare("SELECT COUNT(*) as c FROM mutasi").get().c;
      aktifkanFastMode(now);
    } else {
      console.log(`[POLL] ${waktuStr} | qris_history OK | Saldo: ${formatRupiah(newBalance)} | Tidak ada tx baru`);
    }

  // ===== MODE 2: fallback balance diff =====
  } else {
    const alasan = qris_history?.message || "qris_history tidak tersedia";
    pollingStatus.sumber_data = `balance_diff (${alasan})`;

    const lastBalance = swapLastBalance(newBalance);

    if (lastBalance !== null && newBalance !== lastBalance) {
      const selisih = newBalance - lastBalance;
      const jenis = selisih > 0 ? "masuk" : "keluar";

      console.log(`[MUTASI DIFF] ${waktuStr} | ${jenis.toUpperCase()} | ${formatRupiah(Math.abs(selisih))} | Saldo: ${formatRupiah(newBalance)}`);

      const ins = db.prepare(`
        INSERT OR IGNORE INTO mutasi
          (waktu, waktu_ts, saldo_sebelum, saldo_sesudah, selisih, jenis, sumber)
        VALUES (?, ?, ?, ?, ?, ?, 'balance_diff')
      `).run(waktuStr, now, lastBalance, newBalance, Math.abs(selisih), jenis);

      if (ins.changes > 0) {
        pollingStatus.total_mutasi = db.prepare("SELECT COUNT(*) as c FROM mutasi").get().c;
        aktifkanFastMode(now);
      } else {
        console.log(`[DEDUP] balance_diff duplikat diabaikan (${formatRupiah(Math.abs(selisih))} ${jenis})`);
      }

    } else if (lastBalance === null) {
      console.log(`[POLL] Saldo awal: ${formatRupiah(newBalance)}`);
    } else {
      console.log(`[POLL] ${waktuStr} | Tidak ada perubahan | Saldo: ${formatRupiah(newBalance)}`);
    }
  }

  if (isFastMode && Date.now() >= fastModeUntil) {
    isFastMode = false;
    pollingStatus.fast_mode_selesai = null;
    console.log("[FAST MODE] Selesai, kembali ke normal");
  }
}

function aktifkanFastMode(now) {
  isFastMode = true;
  fastModeUntil = now + config.fast_duration_sec * 1000;
  pollingStatus.fast_mode_selesai = new Date(fastModeUntil).toLocaleString("id-ID", { timeZone: "Asia/Jakarta" });
  console.log(`[FAST MODE] Aktif ${config.fast_duration_sec}s (tiap ${config.fast_interval_sec}s)`);
}

function scheduleNext() {
  if (pollingTimer) clearTimeout(pollingTimer);

  const now = Date.now();
  let interval;

  if (isFastMode && now < fastModeUntil) {
    interval = config.fast_interval_sec * 1000;
    pollingStatus.mode = "cepat";
    pollingStatus.interval_detik = config.fast_interval_sec;
  } else {
    if (isFastMode) {
      isFastMode = false;
      pollingStatus.fast_mode_selesai = null;
      console.log("[FAST MODE] Selesai, kembali ke normal");
    }
    interval = config.normal_interval_sec * 1000;
    pollingStatus.mode = "normal";
    pollingStatus.interval_detik = config.normal_interval_sec;
  }

  pollingTimer = setTimeout(doPoll, interval);
}

// ==================== EXPRESS ROUTES ====================
app.use(express.static(path.join(__dirname, "public")));
app.use(express.json());

app.get("/mutasi", (req, res) => res.sendFile(path.join(__dirname, "public", "mutasi.html")));
app.get("/raw",    (req, res) => res.sendFile(path.join(__dirname, "public", "raw.html")));

app.get("/api/mutasi", (req, res) => {
  const limit = parseInt(req.query.limit) || 10;
  const page = parseInt(req.query.page) || 1;
  const offset = (page - 1) * limit;

  const rows = db
    .prepare("SELECT * FROM mutasi ORDER BY waktu_ts DESC LIMIT ? OFFSET ?")
    .all(limit, offset);

  const total = db.prepare("SELECT COUNT(*) as c FROM mutasi").get().c;

  const caller = req.headers["x-forwarded-for"] || req.socket.remoteAddress || "?";
  console.log(`[API] GET /api/mutasi — total DB: ${total}, return: ${rows.length} baris, page: ${page}, dari: ${caller}`);

  res.json({ success: true, total, page, data: rows });
});

app.get("/api/status", (req, res) => {
  res.json({ success: true, data: pollingStatus });
});

app.get("/api/config", (req, res) => {
  res.json({
    success: true,
    data: {
      normal_interval_sec: config.normal_interval_sec,
      fast_interval_sec: config.fast_interval_sec,
      fast_duration_sec: config.fast_duration_sec,
      proxy_mode: config.proxy_mode || "proxy",
    },
  });
});

app.post("/api/config", (req, res) => {
  const { normal_interval_sec, fast_interval_sec, fast_duration_sec, proxy_mode } = req.body;
  if (normal_interval_sec >= 1) config.normal_interval_sec = parseInt(normal_interval_sec);
  if (fast_interval_sec >= 1)   config.fast_interval_sec   = parseInt(fast_interval_sec);
  if (fast_duration_sec >= 1)   config.fast_duration_sec   = parseInt(fast_duration_sec);
  if (proxy_mode && ["proxy", "direct", "auto"].includes(proxy_mode)) {
    config.proxy_mode = proxy_mode;
    // Kalau mode butuh proxy tapi belum ada, cari dulu
    if (proxy_mode !== "direct" && !activeProxy && !proxyTesting) {
      findWorkingProxy().catch(() => {});
    }
  }

  console.log(`[CONFIG] normal=${config.normal_interval_sec}s | fast=${config.fast_interval_sec}s | durasi=${config.fast_duration_sec}s | mode-proxy=${config.proxy_mode}`);
  res.json({
    success: true,
    message: "Config berhasil diupdate",
    data: {
      normal_interval_sec: config.normal_interval_sec,
      fast_interval_sec: config.fast_interval_sec,
      fast_duration_sec: config.fast_duration_sec,
      proxy_mode: config.proxy_mode,
    },
  });
});

app.post("/api/poll-now", async (req, res) => {
  try {
    await doPollInner();
    scheduleNext(); // reset timer
    res.json({ success: true, message: "Polling dijalankan", cek_terakhir: pollingStatus.cek_terakhir });
  } catch (err) {
    res.json({ success: false, message: err.message });
  }
});

app.delete("/api/mutasi", (req, res) => {
  db.prepare("DELETE FROM mutasi").run();
  db.prepare("DELETE FROM polling_log").run();
  db.prepare("DELETE FROM settings WHERE key = 'last_balance'").run();
  pollingStatus.total_mutasi = 0;
  console.log("[RESET] Semua data dihapus");
  res.json({ success: true, message: "Semua data berhasil dihapus" });
});

app.get("/api/raw", (req, res) => {
  if (!lastRawResponse) {
    return res.json({
      success: false,
      message: "Belum ada response API. Server mungkin baru restart atau API belum berhasil dipanggil.",
    });
  }
  res.json(lastRawResponse);
});

// FIX: error handler global → selalu return JSON, bukan HTML Passenger
app.use((err, req, res, next) => {
  console.error(`[EXPRESS ERROR] ${req.method} ${req.url} — ${err.message}`);
  res.status(500).json({ success: false, error: err.message || "Internal server error" });
});

// ==================== START ====================
app.listen(config.port, "0.0.0.0", () => {
  console.log(`[SERVER] Berjalan di port ${config.port}`);
  console.log(`[CONFIG] Normal: ${config.normal_interval_sec}s | Fast: ${config.fast_interval_sec}s selama ${config.fast_duration_sec}s`);
  pollingStatus.total_mutasi = db.prepare("SELECT COUNT(*) as c FROM mutasi").get().c;

  if (!pollingStarted) {
    pollingStarted = true;
    const startMode = config.proxy_mode || "proxy";
    if (startMode !== "direct") {
      // Cari proxy gratis dulu, baru mulai polling
      findWorkingProxy().then(() => doPoll());
    } else {
      console.log("[PROXY] Mode: direct (tanpa proxy)");
      doPoll();
    }
  }
});
