import { buildStorageState } from "./storage-state.js";

const CONTENT_SCRIPT_ID = "haixun-bridge";
const GO_API_SUCCESS_CODE = 102000;

function unwrapGoApiResponse(raw) {
  if (raw && typeof raw === "object" && "code" in raw) {
    if (raw.code !== GO_API_SUCCESS_CODE) {
      throw new Error(raw.message || `API error ${raw.code}`);
    }
    return raw.data && typeof raw.data === "object" ? raw.data : {};
  }
  return raw && typeof raw === "object" ? raw : {};
}

function parseApiError(raw, status) {
  if (raw && typeof raw === "object") {
    if (typeof raw.message === "string" && raw.message.trim()) return raw.message;
    if (typeof raw.error === "string" && raw.error.trim()) return raw.error;
  }
  return `匯入失敗（HTTP ${status}）`;
}

function equivalentOrigins(origin) {
  const url = new URL(origin);
  const port = url.port ? `:${url.port}` : "";
  const origins = new Set([url.origin]);
  if (url.hostname === "localhost") {
    origins.add(`${url.protocol}//127.0.0.1${port}`);
  } else if (url.hostname === "127.0.0.1") {
    origins.add(`${url.protocol}//localhost${port}`);
  }
  return [...origins];
}

/** The extension uses the page's /api proxy in every environment. */
function resolveApiBases(serverUrl) {
  const url = new URL(serverUrl);
  return [url.origin];
}

async function requestHostPermission(serverOrigin) {
  const origins = equivalentOrigins(serverOrigin).map((item) => `${item}/*`);
  try {
    const granted = await chrome.permissions.contains({ origins });
    if (granted) return true;
    // 無使用者手勢時 request 可能失敗；設定頁從 content script 進來常無 gesture
    return await chrome.permissions.request({ origins });
  } catch {
    return false;
  }
}

async function queryHaixunTabs(serverOrigin) {
  const tabsById = new Map();
  for (const origin of equivalentOrigins(serverOrigin)) {
    try {
      const tabs = await chrome.tabs.query({ url: `${origin}/*` });
      for (const tab of tabs) {
        if (tab.id != null) tabsById.set(tab.id, tab);
      }
    } catch {
      // no permission for that origin
    }
  }
  return [...tabsById.values()];
}

async function ensureContentScript(serverOrigin) {
  const patterns = equivalentOrigins(serverOrigin).map((item) => `${item}/*`);
  try {
    const existing = await chrome.scripting.getRegisteredContentScripts();
    if (existing.some((script) => script.id === CONTENT_SCRIPT_ID)) {
      try {
        await chrome.scripting.unregisterContentScripts({ ids: [CONTENT_SCRIPT_ID] });
      } catch {
        // ignore
      }
    }
    await chrome.scripting.registerContentScripts([
      {
        id: CONTENT_SCRIPT_ID,
        matches: patterns,
        js: ["content-haixun.js"],
        runAt: "document_start",
      },
    ]);
  } catch (error) {
    console.warn("[haixun] registerContentScripts", error);
  }
}

async function injectBridgeIntoTabs(serverOrigin) {
  const tabs = await queryHaixunTabs(serverOrigin);
  for (const tab of tabs) {
    if (!tab.id) continue;
    try {
      await chrome.scripting.executeScript({
        target: { tabId: tab.id },
        files: ["content-haixun.js"],
      });
    } catch {
      // Ignore: no host permission / restricted page / already injected
    }
  }
}

/** 從巡樓分頁讀 JWT；支援舊 key 與新版 hb_live_tokens */
async function readAuthFromHaixunTab(serverOrigin) {
  const tabs = await queryHaixunTabs(serverOrigin);
  for (const tab of tabs) {
    if (!tab.id) continue;
    try {
      const [result] = await chrome.scripting.executeScript({
        target: { tabId: tab.id },
        func: () => {
          let accessToken = localStorage.getItem("haixun.access_token") ?? "";
          if (!accessToken) {
            try {
              const raw = localStorage.getItem("hb_live_tokens");
              if (raw) {
                const parsed = JSON.parse(raw);
                accessToken = String(parsed?.access_token ?? "");
              }
            } catch {
              // ignore
            }
          }
          const pathname = location.pathname ?? "";
          const fromPath = pathname.match(/\/threads\/([^/]+)/)?.[1] ?? "";
          const accountId =
            localStorage.getItem("haixun.active_threads_account_id") ||
            localStorage.getItem("hb_active_threads_account_id") ||
            fromPath ||
            "";
          return {
            accessToken,
            accountId: String(accountId),
            origin: location.origin,
          };
        },
      });
      const payload = result?.result;
      if (payload?.accessToken) {
        return payload;
      }
    } catch (error) {
      console.warn("[haixun] readAuth tab failed (host permission?)", tab.url, error);
    }
  }
  return null;
}

async function postJSON(apiBase, path, accessToken, body) {
  const res = await fetch(`${apiBase}${path}`, {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      Accept: "application/json",
      Authorization: `Bearer ${accessToken}`,
    },
    body: JSON.stringify(body),
  });
  const raw = await res.json().catch(() => ({}));
  if (!res.ok) {
    const err = new Error(parseApiError(raw, res.status));
    err.status = res.status;
    err.raw = raw;
    throw err;
  }
  return unwrapGoApiResponse(raw);
}

/**
 * 優先 scout crawler-session；失敗再試舊 threads session/import（後端已相容）。
 */
async function postSessionToBackend(apiBase, accessToken, storageState, accountId) {
  try {
    await postJSON(apiBase, "/api/v1/scout/crawler-session", accessToken, {
      token: storageState,
    });
    return {
      valid: true,
      synced: true,
      account_id: accountId || "",
      message: `Chrome session 已同步到巡樓爬蟲（${apiBase}）`,
      apiBase,
    };
  } catch (first) {
    // 404/405：舊後端或路由未就緒 → 走 session/import
    const status = first?.status;
    if (status !== 404 && status !== 405) {
      throw first;
    }
    if (!accountId) {
      throw first;
    }
    const data = await postJSON(
      apiBase,
      `/api/v1/threads-accounts/${encodeURIComponent(accountId)}/session/import`,
      accessToken,
      { storageState },
    );
    return {
      valid: data.valid !== false,
      synced: data.synced !== false,
      account_id: data.account_id || accountId,
      username: data.username,
      message: data.message || `Chrome session 已同步（${apiBase}）`,
      apiBase,
    };
  }
}

/**
 * 同步：收集 Threads cookies → 寫入巡樓 scout crawler-session（dev 模式海巡用）
 */
export async function syncThreadsSessionToGo(serverUrl, accountId, accessToken) {
  const origin = new URL(serverUrl).origin;

  if (!accessToken) {
    const hint = equivalentOrigins(origin).join(" 或 ");
    throw new Error(
      `找不到巡樓登入狀態。請在 Chrome 開啟並登入 ${hint}，或在設定頁按「從 Chrome 同步」（會帶上 JWT）`,
    );
  }

  let storageState;
  try {
    storageState = await buildStorageState();
  } catch (error) {
    throw error;
  }

  const bases = resolveApiBases(serverUrl);
  let lastErr = null;
  for (const apiBase of bases) {
    try {
      return await postSessionToBackend(apiBase, accessToken, storageState, accountId);
    } catch (error) {
      lastErr = error;
      const msg = error instanceof Error ? error.message : String(error);
      if (/fetch|network|failed|Failed to fetch/i.test(msg)) {
        continue; // try next api base
      }
      // auth / business error — don't retry other ports
      throw error;
    }
  }

  const tried = bases.join("、");
  const detail = lastErr instanceof Error ? lastErr.message : String(lastErr ?? "");
  throw new Error(
    `無法連線後端（已試 ${tried}）。請確認 gateway 有在跑，且擴充已授權該網址。${detail ? ` ${detail}` : ""}`,
  );
}

async function resolveServerUrl(partial) {
  if (partial) return new URL(partial).origin;

  const stored = await chrome.storage.sync.get(["serverUrl"]);
  if (stored.serverUrl) return new URL(stored.serverUrl).origin;

  const tabs = await chrome.tabs.query({ active: true, currentWindow: true });
  const activeUrl = tabs[0]?.url;
  if (activeUrl) {
    try {
      const origin = new URL(activeUrl).origin;
      if (activeUrl.includes("/app/") || activeUrl.includes("/threads/")) {
        return origin;
      }
    } catch {
      // ignore
    }
  }

  throw new Error("請在擴充功能選項設定巡樓網址（例如 http://localhost:5173），或先開啟巡樓分頁");
}

chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
  // 僅收集 cookies / storageState，由網頁用自己的 JWT 上傳（避免 401）
  if (message?.action === "collect") {
    (async () => {
      try {
        const storageState = await buildStorageState();
        sendResponse({
          success: true,
          storageState,
          message: "已收集 Threads cookies",
        });
      } catch (error) {
        sendResponse({
          success: false,
          message: error instanceof Error ? error.message : "收集失敗",
        });
      }
    })();
    return true;
  }

  if (message?.action !== "sync") return undefined;

  (async () => {
    try {
      const serverUrl = await resolveServerUrl(message.serverUrl);
      // 權限請求可能因無 user gesture 失敗；不因此中斷（cookies 仍可能夠用）
      await requestHostPermission(serverUrl);
      await ensureContentScript(serverUrl);
      await injectBridgeIntoTabs(serverUrl);

      let accountId = String(message.accountId ?? "").trim();
      let accessToken = String(message.accessToken ?? "").trim();
      if (!accountId || !accessToken) {
        const auth = await readAuthFromHaixunTab(serverUrl);
        if (auth) {
          accountId = accountId || String(auth.accountId ?? "").trim();
          accessToken = accessToken || String(auth.accessToken ?? "").trim();
        }
      }
      if (!accessToken) {
        throw new Error(
          "找不到巡樓 JWT。請在設定頁按「從 Chrome 同步」（由網頁帶登入態上傳），或先確認已登入巡樓。",
        );
      }

      const result = await syncThreadsSessionToGo(serverUrl, accountId, accessToken);
      sendResponse({
        success: true,
        valid: result.valid !== false,
        synced: result.synced ?? true,
        account_id: result.account_id,
        username: result.username,
        message: result.message || "Chrome session 已同步",
      });
    } catch (error) {
      sendResponse({
        success: false,
        valid: false,
        message: error instanceof Error ? error.message : "同步失敗",
      });
    }
  })();

  return true;
});

async function bootstrapBridgeFromStorage() {
  try {
    const { serverUrl } = await chrome.storage.sync.get(["serverUrl"]);
    // 正式站優先，並保留本機開發網址。
    const defaults = [
      "https://threads-tool.30cm.net",
      "http://localhost:5173",
      "http://127.0.0.1:5173",
    ];
    const primary = serverUrl || defaults[0];
    if (!serverUrl) {
      await chrome.storage.sync.set({ serverUrl: primary });
    }
    // 註冊並注入目前設定 + 常見網址
    const origins = new Set([primary, ...defaults]);
    for (const origin of origins) {
      try {
        await ensureContentScript(origin);
        await injectBridgeIntoTabs(origin);
      } catch (e) {
        console.warn("[haixun] bootstrap", origin, e);
      }
    }
  } catch (e) {
    console.warn("[haixun] bootstrapBridgeFromStorage", e);
  }
}

chrome.runtime.onInstalled.addListener(() => {
  void bootstrapBridgeFromStorage();
});

chrome.runtime.onStartup.addListener(() => {
  void bootstrapBridgeFromStorage();
});

// service worker 冷啟動也試一次
void bootstrapBridgeFromStorage();
