import { humanDelay, THREADS_MAX_CHARS } from "@/lib/utils";
import { attachImages } from "./attach-media";
import { typeIntoEditor } from "./format-text";
import {
  composeFailureMessage,
  findComposeEditor,
  openComposeSurface,
} from "./compose";
import { withPage } from "./browser";
import { createDebugRun, captureDebugStep, shouldRunHeadedForPublish, type DebugRun } from "./debug";
import { browserSessionOptions, type ActiveSession } from "./session";
import type { PublishResult } from "./types";

export async function publish(
  session: ActiveSession,
  text: string,
  imagePaths: string[] = []
): Promise<PublishResult> {
  if (text.length > THREADS_MAX_CHARS) {
    return { success: false, error: `貼文超過 ${THREADS_MAX_CHARS} 字上限` };
  }

  const debugRun = await createDebugRun("publish");
  const headed = await shouldRunHeadedForPublish();

  return withPage(
    session.storageState,
    async (page) => {
      const compose = await openComposeSurface(page, debugRun);
      if (!compose.ready) {
        await captureDebugStep(page, debugRun, "compose-blocked", { ...compose });
        return {
          success: false,
          error: compose.message ?? composeFailureMessage(compose.reason),
          debugRunId: debugRun?.id,
        };
      }

      const editor = await findComposeEditor(page, debugRun);
      if (!editor) {
        return {
          success: false,
          error: composeFailureMessage("editor_not_found"),
          debugRunId: debugRun?.id,
        };
      }

      if (imagePaths.length > 0) {
        let imageAttached = await attachImages(page, imagePaths, debugRun);
        let textAlreadyEntered = false;

        if (!imageAttached) {
          await typeIntoEditor(page, editor, text);
          textAlreadyEntered = true;
          await captureDebugStep(page, debugRun, "text-entered-before-image", {
            length: text.length,
          });
          imageAttached = await attachImages(page, imagePaths, debugRun);
        }

        if (!imageAttached) {
          return {
            success: false,
            error: "無法附加配圖。建議使用 JPG/PNG，或開啟 Debug 模式查看上傳步驟。",
            debugRunId: debugRun?.id,
          };
        }

        await humanDelay(1200, 2000);

        if (!textAlreadyEntered) {
          const editorAfterImage = (await findComposeEditor(page, debugRun, 8000)) ?? editor;
          await typeIntoEditor(page, editorAfterImage, text);
        }

        await captureDebugStep(page, debugRun, "text-entered", { length: text.length });
        await humanDelay(800, 1500);
      } else {
        await editor.click();
        await humanDelay(300, 600);
        await typeIntoEditor(page, editor, text);
        await captureDebugStep(page, debugRun, "text-entered", { length: text.length });
        await humanDelay(800, 1500);
      }

      const posted = await clickPublishButton(page);
      await captureDebugStep(page, debugRun, "post-click", { posted });
      if (!posted) {
        return {
          success: false,
          error: "找不到可點擊的發佈按鈕",
          debugRunId: debugRun?.id,
        };
      }

      const confirmed = await waitForPublishComplete(page, imagePaths.length > 0);
      if (!confirmed) {
        return {
          success: false,
          error: "發佈可能未完成，請到 Threads 確認是否已出現貼文",
          debugRunId: debugRun?.id,
        };
      }

      await humanDelay(1500, 2500);
      const permalink =
        (await extractLatestPermalink(page)) ??
        (session.username
          ? await extractPermalinkFromProfile(page, text, session.username, debugRun)
          : null);

      await captureDebugStep(page, debugRun, "publish-success", { permalink });
      return { success: true, permalink: permalink ?? undefined, debugRunId: debugRun?.id };
    },
    { ...browserSessionOptions(session), headless: !headed }
  );
}

async function clickPublishButton(page: import("playwright").Page): Promise<boolean> {
  const scopes = [page.locator('[role="dialog"]'), page.locator("body")];

  const postSelectors = [
    '[aria-label="發佈"]',
    '[aria-label="發布"]',
    '[aria-label="Post"]',
    'div[role="button"]:has-text("發佈")',
    'div[role="button"]:has-text("發布")',
    'div[role="button"]:has-text("Post")',
    'button:has-text("發佈")',
    'button:has-text("發布")',
    'button:has-text("Post")',
  ];

  for (const scope of scopes) {
    for (const selector of postSelectors) {
      const buttons = scope.locator(selector);
      const count = await buttons.count();
      for (let i = count - 1; i >= 0; i--) {
        const btn = buttons.nth(i);
        if (!(await btn.isVisible({ timeout: 1200 }).catch(() => false))) continue;
        const disabled = await btn.getAttribute("aria-disabled");
        if (disabled === "true") continue;
        const label = (await btn.innerText().catch(() => "")).trim();
        if (!label || label.length > 12) continue;
        await btn.click();
        return true;
      }
    }
  }

  return false;
}

async function waitForPublishComplete(
  page: import("playwright").Page,
  hasImage = false
): Promise<boolean> {
  await humanDelay(hasImage ? 4000 : 2500, hasImage ? 6500 : 4000);

  const errorToast = page
    .locator("text=/發佈失敗|發布失敗|無法發佈|couldn't post|failed to post/i")
    .first();
  if (await errorToast.isVisible({ timeout: 2000 }).catch(() => false)) {
    return false;
  }

  const dialog = page.locator('[role="dialog"]');
  if ((await dialog.count()) > 0) {
    const closed = await dialog
      .first()
      .waitFor({ state: "hidden", timeout: hasImage ? 30000 : 18000 })
      .then(() => true)
      .catch(() => false);
    if (closed) return true;
  }

  const composeEditor = page.locator('[role="dialog"] [contenteditable="true"]').first();
  if ((await composeEditor.count()) > 0) {
    const content = await composeEditor.innerText().catch(() => "");
    if (content.trim().length < 3) return true;
  }

  if (!page.url().includes("/compose") && !page.url().includes("intent/post")) return true;

  const toast = page.locator("text=/已發佈|已發布|posted|Post published/i").first();
  return toast.isVisible({ timeout: 5000 }).catch(() => false);
}

async function extractLatestPermalink(page: import("playwright").Page): Promise<string | null> {
  try {
    const link = page.locator('a[href*="/post/"]').first();
    const href = await link.getAttribute("href", { timeout: 5000 });
    if (!href) return null;
    return href.startsWith("http") ? href : `https://www.threads.com${href}`;
  } catch {
    return null;
  }
}

async function extractPermalinkFromProfile(
  page: import("playwright").Page,
  text: string,
  username: string,
  debug?: DebugRun | null
): Promise<string | null> {
  const snippet = text.trim().slice(0, 48);
  if (!snippet) return null;

  try {
    await page.goto(`https://www.threads.com/@${username}`, {
      waitUntil: "domcontentloaded",
      timeout: 30000,
    });
    await humanDelay(2000, 3000);
    await captureDebugStep(page, debug, "profile-check");

    const postLinks = page.locator('a[href*="/post/"]');
    const count = Math.min(await postLinks.count(), 8);
    for (let i = 0; i < count; i++) {
      const link = postLinks.nth(i);
      const container = link.locator(
        "xpath=ancestor::*[contains(@data-pressable-container,'true')][1]"
      );
      const scope = (await container.count()) > 0 ? container : link;
      const body = await scope.innerText().catch(() => "");
      if (body.includes(snippet.slice(0, 24))) {
        const href = await link.getAttribute("href");
        if (href) return href.startsWith("http") ? href : `https://www.threads.com${href}`;
      }
    }

    const fallback = page.locator('a[href*="/post/"]').first();
    const href = await fallback.getAttribute("href", { timeout: 5000 });
    if (!href) return null;
    return href.startsWith("http") ? href : `https://www.threads.com${href}`;
  } catch {
    return null;
  }
}