import { existsSync } from "fs";
import { mkdir, rm } from "fs/promises";
import path from "path";
import { chromium, type Browser, type BrowserContext, type BrowserContextOptions } from "playwright";
import { shouldRunHeaded, getPlaywrightSlowMo } from "./debug";
import { humanPause } from "./human-behavior";
import { withSessionLock } from "./session-lock";

const USER_AGENT =
  "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36";

export const THREADS_PROFILE_DIR = path.join(process.cwd(), "data", "threads-browser-profile");

const BROWSER_ARGS = [
  "--disable-blink-features=AutomationControlled",
  "--no-first-run",
  "--no-default-browser-check",
];

const STEALTH_INIT_SCRIPT = () => {
  Object.defineProperty(navigator, "webdriver", { get: () => false });
};

let browserInstance: Browser | null = null;

async function isHeadless() {
  if (process.env.PLAYWRIGHT_HEADLESS === "false") return false;
  if (process.env.PLAYWRIGHT_HEADLESS === "true") return true;
  return !(await shouldRunHeaded());
}

export function hasPersistentProfile(): boolean {
  return (
    existsSync(path.join(THREADS_PROFILE_DIR, "Default")) ||
    existsSync(path.join(THREADS_PROFILE_DIR, "Local State"))
  );
}

export async function ensureProfileDir() {
  await mkdir(THREADS_PROFILE_DIR, { recursive: true });
}

/** 清除 Playwright 持久化 profile，避免舊 session 與手機/瀏覽器的 IG 登入互相踢出。 */
export async function clearBrowserProfile(): Promise<{ cleared: boolean; message: string }> {
  if (!hasPersistentProfile()) {
    return { cleared: false, message: "瀏覽器 session 本來就是空的" };
  }

  await rm(THREADS_PROFILE_DIR, { recursive: true, force: true });
  await ensureProfileDir();
  return { cleared: true, message: "已清除瀏覽器 session" };
}

export async function getBrowser(): Promise<Browser> {
  if (!browserInstance || !browserInstance.isConnected()) {
    browserInstance = await chromium.launch({
      headless: await isHeadless(),
      slowMo: getPlaywrightSlowMo(),
      args: BROWSER_ARGS,
    });
  }
  return browserInstance;
}

export async function createContext(
  storageState?: string,
  headless?: boolean
): Promise<BrowserContext> {
  const resolvedHeadless = headless ?? (await isHeadless());
  const browser = resolvedHeadless
    ? await getBrowser()
    : await chromium.launch({
        headless: false,
        slowMo: getPlaywrightSlowMo(),
        args: BROWSER_ARGS,
      });

  let state: BrowserContextOptions["storageState"];
  if (storageState) {
    try {
      state = JSON.parse(storageState);
    } catch {
      throw new Error("瀏覽器 session 資料損毀，請到連線設定重新同步");
    }
  }

  const context = await browser.newContext({
    storageState: state,
    userAgent: USER_AGENT,
    viewport: { width: 1280, height: 900 },
    locale: "zh-TW",
    timezoneId: "Asia/Taipei",
  });

  await context.addInitScript(STEALTH_INIT_SCRIPT);
  return context;
}

export async function launchSessionContext(options?: {
  storageState?: string;
  headless?: boolean;
}): Promise<BrowserContext> {
  const headless = options?.headless ?? (await isHeadless());

  if (options?.storageState) {
    return createContext(options.storageState, headless);
  }

  await ensureProfileDir();
  const context = await chromium.launchPersistentContext(THREADS_PROFILE_DIR, {
    headless,
    slowMo: getPlaywrightSlowMo(),
    args: BROWSER_ARGS,
    userAgent: USER_AGENT,
    viewport: { width: 1280, height: 900 },
    locale: "zh-TW",
    timezoneId: "Asia/Taipei",
  });
  await context.addInitScript(STEALTH_INIT_SCRIPT);
  return context;
}

export async function launchLoginContext(): Promise<BrowserContext> {
  await ensureProfileDir();
  const context = await chromium.launchPersistentContext(THREADS_PROFILE_DIR, {
    headless: false,
    slowMo: getPlaywrightSlowMo(),
    args: BROWSER_ARGS,
    userAgent: USER_AGENT,
    viewport: { width: 1280, height: 900 },
    locale: "zh-TW",
    timezoneId: "Asia/Taipei",
  });
  await context.addInitScript(STEALTH_INIT_SCRIPT);
  return context;
}

export interface BrowserSessionOptions {
  headless?: boolean;
  accountId?: string;
  storageState?: string;
  persistSession?: boolean;
  onSessionPersisted?: (storageState: string) => void | Promise<void>;
}

async function persistContextSession(
  context: BrowserContext,
  options?: BrowserSessionOptions
) {
  if (!options?.accountId || options.persistSession === false) return;
  const state = await context.storageState();
  const serialized = JSON.stringify(state);
  await options.onSessionPersisted?.(serialized);
}

export async function withSharedContext<T>(
  storageState: string | undefined,
  fn: (context: BrowserContext) => Promise<T>,
  options?: BrowserSessionOptions
): Promise<T> {
  return withSessionLock(async () => {
    const headless = options?.headless ?? (await isHeadless());
    const context = await launchSessionContext({
      storageState: storageState ?? options?.storageState,
      headless,
    });

    try {
      await humanPause(Math.random() < 0.7 ? "micro" : "short");
      const result = await fn(context);
      await persistContextSession(context, options);
      return result;
    } finally {
      await context.close();
    }
  });
}

export async function withPage<T>(
  storageState: string | undefined,
  fn: (page: import("playwright").Page) => Promise<T>,
  options?: BrowserSessionOptions
): Promise<T> {
  return withSharedContext(
    storageState,
    async (context) => {
      const page = context.pages()[0] ?? (await context.newPage());
      return fn(page);
    },
    options
  );
}