/**
 * iCloud Local — Embedded iCloud Hide My Email + Inbox
 * Menggantikan dependency https://icloud.studiolabs.web.id/
 * Menggunakan Apple Mail Web Service 2 (mailws2) via REST API
 */

import { readFileSync, writeFileSync, existsSync } from "fs";
import { join, dirname } from "path";
import { fileURLToPath } from "url";
import crypto from "crypto";

const __dirname = dirname(fileURLToPath(import.meta.url));
const DATA_DIR = join(__dirname, "..", "icloud");
const ACCOUNTS_FILE = join(DATA_DIR, "accounts.json");
const MEMBERS_FILE = join(DATA_DIR, "members.json");
const TRACKED_FILE = join(DATA_DIR, "tracked_emails.json");

// ============ File I/O ============
function readJSON(filepath) {
  if (!existsSync(filepath)) return {};
  try {
    return JSON.parse(readFileSync(filepath, "utf8"));
  } catch {
    return {};
  }
}
function writeJSON(filepath, data) {
  writeFileSync(filepath, JSON.stringify(data, null, 2), "utf8");
}

// ============ Account helpers ============
export function getAccounts() {
  const data = readJSON(ACCOUNTS_FILE);
  return data.accounts || [];
}

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

export function saveAccount(accData) {
  const data = readJSON(ACCOUNTS_FILE);
  const accounts = data.accounts || [];
  const idx = accounts.findIndex(a => a.id === accData.id);
  const now = new Date().toISOString();
  if (idx >= 0) {
    // Update existing
    accounts[idx] = { ...accounts[idx], ...accData, updatedAt: now };
  } else {
    // Add new
    accounts.push({
      id: accData.id || crypto.randomBytes(8).toString("hex"),
      name: accData.name || "",
      subdomain: accData.subdomain || "p188-maildomainws.icloud.com",
      dsid: accData.dsid || "",
      clientId: accData.clientId || "",
      cookies: accData.cookies || "",
      email: accData.email || "",
      appPassword: accData.appPassword || "",
      createdAt: now,
    });
  }
  writeJSON(ACCOUNTS_FILE, { accounts });
  return getAccountById(accData.id || accounts[accounts.length - 1].id);
}

export function deleteAccount(accountId) {
  const data = readJSON(ACCOUNTS_FILE);
  const accounts = data.accounts || [];
  const idx = accounts.findIndex(a => a.id === accountId);
  if (idx < 0) return false;
  accounts.splice(idx, 1);
  writeJSON(ACCOUNTS_FILE, { accounts });
  return true;
}

// ============ Member helpers ============
export function getMembers() {
  const data = readJSON(MEMBERS_FILE);
  return data.members || [];
}

export function findMemberByToken(token) {
  if (!token) return null;
  return getMembers().find(m => m.sessionToken === token) || null;
}

export function requireMemberToken(token, deviceId) {
  const member = findMemberByToken(token);
  if (!member) throw { status: 401, error: "Sesi tidak valid. Mungkin login dari device lain." };
  if ((member.status || "") === "revoked") throw { status: 403, error: "Akses dicabut. Hubungi admin." };
  if (!member.expiresAt || new Date(member.expiresAt) <= new Date()) {
    throw { status: 403, error: "Masa langganan sudah berakhir." };
  }
  if (member.currentDeviceId && deviceId && member.currentDeviceId !== deviceId) {
    throw { status: 401, error: "Sesi diambil alih oleh device lain." };
  }
  return member;
}

// ============ Tracked emails ============
export function getTrackedEmails(memberId) {
  const data = readJSON(TRACKED_FILE);
  const emails = data.emails || [];
  if (!memberId) return emails;
  return emails.filter(e => e.memberId === memberId);
}

export function countTodayForMember(memberId) {
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  return getTrackedEmails(memberId).filter(e => {
    const created = new Date(e.createdAt);
    return created >= today;
  }).length;
}

export function addTrackedEmail(memberId, entry) {
  const data = readJSON(TRACKED_FILE);
  const emails = data.emails || [];
  emails.push({
    memberId,
    accountId: entry.accountId || "",
    hme: entry.hme || "",
    anonymousId: entry.anonymousId || `custom_${crypto.randomBytes(16).toString("hex")}`,
    label: entry.label || "",
    note: entry.note || "Custom Domain Email",
    createdAt: entry.createdAt || new Date().toISOString(),
    expiresAt: entry.expiresAt || null,
    pinned: false,
  });
  writeJSON(TRACKED_FILE, { emails });
  return emails[emails.length - 1];
}

// ============ HME Generate (custom domain) ============
function generateCustomEmail(prefix) {
  const clean = (prefix || "").toLowerCase().replace(/[^a-z0-9]/g, "");
  const num = String(Math.floor(Math.random() * 1000)).padStart(3, "0");
  const base = clean || `user${Date.now().toString(36)}`;
  return `${base}${num}@tekno-mail.my.id`;
}

// ============ iCloud Mail WS2 API call ============
async function mailws2Call(account, endpoint, body) {
  const sub = account.subdomain || "p188-maildomainws.icloud.com";
  const parts = sub.split("-", 2);
  const prefix = parts[0] || "p149";
  const sep = endpoint.includes("?") ? "&" : "?";
  const url = `https://${prefix}-mccgateway.icloud.com/mailws2/v1/${endpoint}${sep}clientBuildNumber=2618Build25&clientMasteringNumber=2618Build25&clientId=${encodeURIComponent(account.clientId)
    }&dsid=${encodeURIComponent(account.dsid)}`;

  const resp = await fetch(url, {
    method: "POST",
    headers: {
      accept: "application/json",
      "accept-language": "en-US,en;q=0.9",
      "content-type": "application/json",
      cookie: account.cookies || "",
      origin: "https://www.icloud.com",
      referer: "https://www.icloud.com/",
      "user-agent":
        "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/148.0.0.0 Safari/537.36",
    },
    body: JSON.stringify(body),
  });

  const text = await resp.text();
  let data;
  try {
    data = JSON.parse(text);
  } catch {
    throw new Error(`Mail API returned non-JSON (HTTP ${resp.status})`);
  }
  return { status: resp.status, data };
}

// ============ Inbox: Universal ==========
export async function _fetchUniversalInbox(account) {
  // 1. Thread search
  const threadBody = {
    responseType: "THREAD_DIGEST",
    includeFolderStatus: true,
    maxResults: 100,
    sessionHeaders: {
      folder: "INBOX",
      threadmode: 1,
    },
  };
  const threadRes = await mailws2Call(account, "thread/search", threadBody);
  if (threadRes.status >= 400 || !threadRes.data?.threadList) {
    throw new Error(`Gagal mengambil threads dari iCloud Mail (HTTP ${threadRes.status})`);
  }

  const threads = threadRes.data.threadList;
  const merged = {};
  for (const t of threads) {
    const tid = t.threadId || "";
    if (!tid) continue;
    merged[tid] = {
      id: tid,
      date: t.timestamp ? new Date(t.timestamp).toISOString() : "",
      subject: t.subject || "(Tanpa Subjek)",
      from: t.senders?.length ? t.senders[0] : "Unknown",
      to: "",
      text: t.preview || "",
      html: "",
      folder: "INBOX",
    };
  }

  // 2. Message list for recipient "To" headers
  try {
    const msgBody = {
      domain: "email",
      properties: [
        "uid",
        "appleThrid",
        "identifier",
        "stateInternalDate",
        "mboxRef",
        {
          type: "fieldOf",
          property: "rfc822HeaderBytes",
          value: "From",
          alias: "from",
          decoder: "encodedWords",
        },
        {
          type: "fieldOf",
          property: "rfc822HeaderBytes",
          value: "To",
          decoder: "encodedWords",
          alias: "to",
        },
        {
          type: "fieldOf",
          property: "rfc822HeaderBytes",
          value: "Subject",
          decoder: "encodedWords",
          alias: "subject",
        },
      ],
      limit: 150,
      predicate: {
        expression: { property: "flags", value: "DELETED", type: "fieldOf" },
        value: false,
        type: "eq",
        and: [
          {
            type: "notIn",
            expression: { property: "mboxRef" },
            value: ["32", "35"],
          },
        ],
      },
      orderby: {
        expressions: [{ property: "stateInternalDate", type: "property" }],
        ascending: false,
      },
    };

    const msgRes = await mailws2Call(account, "message/list?clientIntent=fetchMessageList", msgBody);
    if (msgRes.status === 200 && Array.isArray(msgRes.data?.domainObjects)) {
      for (const msg of msgRes.data.domainObjects) {
        const tid = msg.identifier || msg.appleThrid || "";
        if (!tid || !merged[tid]) continue;
        if (msg.to) merged[tid].to = msg.to;
        if (msg.subject) merged[tid].subject = msg.subject;
      }
    }
  } catch (_) {
    // message list optional — thread search sudah cukup
  }

  return Object.values(merged);
}

// ============ Inbox: Thread ============
function _getFolderName(msg) {
  let mboxId = "";
  if (msg.mboxRef) {
    if (typeof msg.mboxRef === "object" && msg.mboxRef.id) {
      mboxId = String(msg.mboxRef.id);
    } else {
      mboxId = String(msg.mboxRef);
    }
  }
  const map = { "34": "Sent", "33": "Drafts", "36": "Junk", "32": "Trash", "35": "Archive" };
  return map[mboxId] || "INBOX";
}

export async function _fetchUniversalThread(account, threadId) {
  // 1. Thread search
  const threadBody = {
    responseType: "THREAD_DIGEST",
    includeFolderStatus: false,
    maxResults: 1,
    sessionHeaders: { folder: "INBOX", threadmode: 1 },
    threadId,
  };

  const threadRes = await mailws2Call(account, "thread/search", threadBody);
  if (threadRes.status >= 400 || !threadRes.data?.threadList) {
    throw new Error(`Gagal mengambil thread (HTTP ${threadRes.status})`);
  }

  const tl = threadRes.data.threadList;
  if (!tl.length) return [];

  // 2. Message list dengan "parts" property untuk dapatkan partId
  const msgBody = {
    domain: "email",
    properties: [
      "uid", "appleThrid", "identifier", "flags", "stateInternalDate", "rfc822Size",
      "hasAttachment", "messageId", "mboxRef", "previewId", "parts", "isSeen", "modseq",
      { type: "fieldOf", property: "rfc822HeaderBytes", value: "From", alias: "from", decoder: "encodedWords" },
      { type: "fieldOf", property: "rfc822HeaderBytes", value: "To", decoder: "encodedWords", alias: "to" },
      { type: "fieldOf", property: "rfc822HeaderBytes", value: "Cc", decoder: "encodedWords", alias: "cc" },
      { type: "fieldOf", property: "rfc822HeaderBytes", value: "Subject", decoder: "encodedWords", alias: "subject" },
      "mboxRefUidMap",
    ],
    limit: 50,
    predicate: {
      expression: { property: "appleThrid", type: "property" },
      value: threadId,
      type: "eq",
    },
    orderby: {
      expressions: [{ property: "stateInternalDate", type: "property" }],
      ascending: true,
    },
  };

  const msgRes = await mailws2Call(account, "message/list?clientIntent=fetchMessageListByThreadId", msgBody);
  if (msgRes.status >= 400 || !Array.isArray(msgRes.data?.domainObjects)) {
    return [];
  }

  let domainObjects = msgRes.data.domainObjects;
  // Max 3 messages terakhir (seperti PHP)
  if (domainObjects.length > 3) {
    domainObjects = domainObjects.slice(-3);
  }

  const messages = [];
  for (const m of domainObjects) {
    const uid = m.uid || "";
    if (!uid) continue;

    const folderName = _getFolderName(m);
    const entry = {
      id: uid,
      threadId,
      date: m.stateInternalDate ? new Date(Number(m.stateInternalDate) * (Number(m.stateInternalDate) < 10000000000 ? 1000 : 1)).toISOString() : "",
      from: m.from || "Unknown",
      to: m.to || "",
      subject: m.subject || "(Tanpa Subjek)",
      folder: folderName,
      text: "",
      html: "",
    };

    // 3. Cari partId untuk text/html & text/plain dari message.parts
    let htmlPartId = "", plainPartId = "";
    if (Array.isArray(m.parts)) {
      for (const part of m.parts) {
        const ct = (part.contentType || "").toLowerCase();
        if (ct === "text/html" && !htmlPartId) {
          htmlPartId = part.partId || "";
        } else if (ct === "text/plain" && !plainPartId) {
          plainPartId = part.partId || "";
        }
      }
    }

    // 4. Fetch body via message/get menggunakan partId (string, bukan object)
    const partIds = [htmlPartId, plainPartId].filter(Boolean);
    for (const partId of partIds) {
      const isHtml = partId === htmlPartId;
      try {
        const getRes = await mailws2Call(account, "message/get", {
          uid,
          parts: [partId],
          sessionHeaders: { folder: folderName, threadmode: 1 },
        });
        if (getRes.status === 200 && Array.isArray(getRes.data?.parts)) {
          const content = getRes.data.parts[0]?.content || "";
          if (isHtml) {
            entry.html = content;
          } else {
            entry.text = content;
          }
        }
      } catch (_) {
        // part fetch gagal — skip
      }
    }

    messages.push(entry);
  }

  return messages;
}

// ============ Express Router ============
export function createIcloudRouter(Router) {
  const router = Router();

  // ----- Auth: redeem voucher -----
  router.post("/auth/redeem", async (req, res) => {
    try {
      const { code, deviceId, deviceInfo } = req.body || {};
      if (!code) return res.status(400).json({ success: false, error: "Kode voucher dibutuhkan" });

      const members = getMembers();
      const member = members.find(
        m => m.voucherCode === code && (m.status === "unused" || m.status === "active")
      );

      if (!member) {
        return res.status(404).json({ success: false, error: "Voucher tidak ditemukan atau sudah digunakan" });
      }

      // Jika sudah ada session token dan belum expired, gunakan yang sudah ada
      if (member.status === "active" && member.sessionToken) {
        const expiresAt = new Date(member.expiresAt);
        if (expiresAt > new Date()) {
          return res.json({
            success: true,
            memberName: member.name,
            sessionToken: member.sessionToken,
            expiresAt: member.expiresAt,
            dailyLimit: member.dailyLimit || 15,
            autoDeleteMin: member.autoDeleteMin || 10,
          });
        }
      }

      // Generate session
      const sessionToken = crypto.randomBytes(24).toString("hex");
      const durationDays = member.durationDays || 7;
      const expiresAt = new Date(Date.now() + durationDays * 86400 * 1000).toISOString();

      member.sessionToken = sessionToken;
      member.expiresAt = expiresAt;
      member.currentDeviceId = deviceId || "";
      member.lastDeviceInfo = deviceInfo || {};
      member.status = "active";
      member.redeemedAt = new Date().toISOString();

      writeJSON(MEMBERS_FILE, { members });

      return res.json({
        success: true,
        memberName: member.name,
        sessionToken,
        expiresAt,
        dailyLimit: member.dailyLimit || 15,
        autoDeleteMin: member.autoDeleteMin || 10,
      });
    } catch (err) {
      return res.status(500).json({ success: false, error: err.message });
    }
  });

  // ----- Auth: get member info -----
  router.get("/auth/me", async (req, res) => {
    try {
      const auth = req.headers.authorization || "";
      const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
      const deviceId = req.headers["x-device-id"] || "";
      const member = requireMemberToken(token, deviceId);
      const expiresAt = new Date(member.expiresAt);
      const remainingDays = Math.max(0, Math.ceil((expiresAt - Date.now()) / 86400));
      return res.json({
        success: true,
        memberName: member.name || "",
        voucherCode: member.voucherCode || "",
        expiresAt: member.expiresAt,
        remainingDays,
        dailyLimit: member.dailyLimit != null ? member.dailyLimit : 15,
        autoDeleteMin: member.autoDeleteMin != null ? member.autoDeleteMin : 10,
        usedToday: countTodayForMember(member.id),
      });
    } catch (err) {
      if (err.status) return res.status(err.status).json({ success: false, error: err.error });
      return res.status(500).json({ success: false, error: err.message });
    }
  });

  // ----- Generate HME email -----
  router.post("/generate", async (req, res) => {
    try {
      const auth = req.headers.authorization || "";
      const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
      const deviceId = req.headers["x-device-id"] || "";
      const accountId = req.headers["x-account-id"] || req.body?.accountId || "";
      const member = requireMemberToken(token, deviceId);

      // Cek daily limit
      const limit = member.dailyLimit != null ? member.dailyLimit : 15;
      if (limit > 0) {
        const used = countTodayForMember(member.id);
        if (used >= limit) {
          return res.status(429).json({
            success: false,
            error: `Batas pembuatan email harian tercapai (${used}/${limit}).`,
            memberLimitReached: true,
            usedToday: used,
            dailyLimit: limit,
          });
        }
      }

      const account = getAccountById(accountId) || getAccounts()[0];
      if (!account) return res.status(500).json({ success: false, error: "Tidak ada akun iCloud tersedia" });

      const prefix = (req.body?.prefix || "").trim();
      const customEmail = generateCustomEmail(prefix);
      const anonId = "custom_" + crypto.createHash("md5").update(customEmail).digest("hex");

      // Auto-track
      const ttl = member.autoDeleteMin != null ? member.autoDeleteMin : 10;
      const ttlExpires = ttl > 0 ? new Date(Date.now() + ttl * 60000).toISOString() : null;
      addTrackedEmail(member.id, {
        accountId: account.id,
        hme: customEmail,
        anonymousId: anonId,
        label: "",
        note: "Custom Domain Email",
        createdAt: new Date().toISOString(),
        expiresAt: ttlExpires,
      });

      return res.json({
        success: true,
        data: { success: true, result: { hme: customEmail } },
      });
    } catch (err) {
      if (err.status) return res.status(err.status).json({ success: false, error: err.error });
      return res.status(500).json({ success: false, error: err.message });
    }
  });

  // ----- Inbox: universal (polling email) -----
  router.get("/inbox/universal", async (req, res) => {
    try {
      const auth = req.headers.authorization || "";
      const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
      const deviceId = req.headers["x-device-id"] || "";
      const accountId = req.headers["x-account-id"] || req.query.accountId || "";
      const member = requireMemberToken(token, deviceId);

      const account = getAccountById(accountId) || getAccounts()[0];
      if (!account) return res.status(500).json({ success: false, error: "Tidak ada akun iCloud tersedia" });

      const threads = await _fetchUniversalInbox(account);
      return res.json({ success: true, data: threads });
    } catch (err) {
      if (err.status) return res.status(err.status).json({ success: false, error: err.error });
      console.error("[iCloud] inbox/universal error:", err.message);
      return res.status(500).json({ success: false, error: err.message });
    }
  });

  // ----- Inbox: thread detail -----
  router.post("/inbox/universal/thread", async (req, res) => {
    try {
      const auth = req.headers.authorization || "";
      const token = auth.startsWith("Bearer ") ? auth.slice(7) : "";
      const deviceId = req.headers["x-device-id"] || "";
      const accountId = req.headers["x-account-id"] || req.body?.accountId || "";
      const threadId = req.body?.threadId || "";
      if (!threadId) return res.status(400).json({ success: false, error: "threadId wajib diisi" });

      const member = requireMemberToken(token, deviceId);
      const account = getAccountById(accountId) || getAccounts()[0];
      if (!account) return res.status(500).json({ success: false, error: "Tidak ada akun iCloud tersedia" });

      const messages = await _fetchUniversalThread(account, threadId);
      return res.json({ success: true, data: messages });
    } catch (err) {
      if (err.status) return res.status(err.status).json({ success: false, error: err.error });
      console.error("[iCloud] inbox/universal/thread error:", err.message);
      return res.status(500).json({ success: false, error: err.message });
    }
  });

  // ----- Test connection -----
  router.get("/test-connection", async (_req, res) => {
    try {
      const account = getAccounts()[0];
      if (!account) return res.json({ ok: false, error: "Tidak ada akun iCloud terdaftar" });

      const testBody = {
        responseType: "THREAD_DIGEST",
        includeFolderStatus: true,
        maxResults: 1,
        sessionHeaders: { folder: "INBOX", threadmode: 1 },
      };
      const testRes = await mailws2Call(account, "thread/search", testBody);
      if (testRes.status < 400) {
        return res.json({ ok: true });
      }
      return res.json({ ok: false, error: `HTTP ${testRes.status} — cookies mungkin expired` });
    } catch (err) {
      return res.json({ ok: false, error: err.message });
    }
  });

  // ----- Health -----
  router.get("/health", async (_req, res) => {
    try {
      const accounts = getAccounts();
      const members = getMembers();
      const tracked = getTrackedEmails();
      return res.json({
        success: true,
        data: {
          accounts: accounts.length,
          members: members.length,
          trackedEmails: tracked.length,
          accountsFile: ACCOUNTS_FILE,
        },
      });
    } catch (err) {
      return res.status(500).json({ success: false, error: err.message });
    }
  });

  return router;
}
