import { execFile } from "child_process";
import { mkdtemp, rm } from "fs/promises";
import { tmpdir } from "os";
import path from "path";
import { promisify } from "util";
import type { Page } from "playwright";
import { humanDelay } from "@/lib/utils";
import { draftImageAbsolutePath } from "@/lib/drafts/images";
import type { DebugRun } from "./debug";
import { captureDebugStep } from "./debug";

const execFileAsync = promisify(execFile);

const ATTACH_TRIGGER_SELECTORS = [
  '[role="dialog"] [aria-label*="附加"]',
  '[role="dialog"] [aria-label*="Attach"]',
  '[role="dialog"] [aria-label*="媒體"]',
  '[role="dialog"] [aria-label*="Media"]',
  '[role="dialog"] [aria-label*="照片"]',
  '[role="dialog"] [aria-label*="Photo"]',
  '[role="dialog"] [aria-label*="相簿"]',
  '[role="dialog"] [aria-label*="Album"]',
  '[role="dialog"] [aria-label*="圖片"]',
  '[role="dialog"] [aria-label*="Image"]',
  '[aria-label*="附加"]',
  '[aria-label*="Attach"]',
  '[aria-label*="媒體"]',
  '[aria-label*="Media"]',
  '[aria-label*="照片"]',
  '[aria-label*="Photo"]',
  '[aria-label*="相簿"]',
  '[aria-label*="Album"]',
  '[aria-label*="圖片"]',
  '[aria-label*="Image"]',
  '[aria-label*="從電腦"]',
  '[aria-label*="Upload"]',
  'div[role="button"]:has-text("附加")',
  'div[role="button"]:has-text("Attach")',
];

const IMAGE_PREVIEW_SELECTORS = [
  '[role="dialog"] img[src^="blob:"]',
  '[role="dialog"] img[src^="data:"]',
  '[role="dialog"] video[src^="blob:"]',
  '[role="dialog"] [aria-label*="移除"]',
  '[role="dialog"] [aria-label*="Remove"]',
  '[role="dialog"] [aria-label*="刪除"]',
  '[role="dialog"] [aria-label*="Delete"]',
  '[role="dialog"] [data-testid*="media"]',
];

export interface PreparedUploadImage {
  path: string;
  cleanup?: () => Promise<void>;
}

export async function prepareImageForBrowserUpload(imagePath: string): Promise<PreparedUploadImage> {
  const absolute = draftImageAbsolutePath(imagePath);
  const ext = path.extname(imagePath).toLowerCase();

  if (ext === ".jpg" || ext === ".jpeg" || ext === ".png") {
    return { path: absolute };
  }

  if (process.platform === "darwin" && (ext === ".webp" || ext === ".gif")) {
    const tempDir = await mkdtemp(path.join(tmpdir(), "threadtools-img-"));
    const outPath = path.join(tempDir, "upload.jpg");
    try {
      await execFileAsync("sips", ["-s", "format", "jpeg", absolute, "--out", outPath]);
      return {
        path: outPath,
        cleanup: async () => {
          await rm(tempDir, { recursive: true, force: true });
        },
      };
    } catch {
      await rm(tempDir, { recursive: true, force: true });
    }
  }

  return { path: absolute };
}

async function dismissCropOrConfirmDialog(page: Page) {
  const labels = [/完成/, /Done/, /加入/, /Add/, /套用/, /Apply/, /確定/, /OK/];
  for (const pattern of labels) {
    const btn = page.getByRole("button", { name: pattern }).first();
    if (await btn.isVisible({ timeout: 400 }).catch(() => false)) {
      await btn.click().catch(() => undefined);
      await humanDelay(500, 900);
    }
  }
}

async function waitForImagePreview(page: Page, debug?: DebugRun | null, timeoutMs = 20000): Promise<boolean> {
  const deadline = Date.now() + timeoutMs;

  while (Date.now() < deadline) {
    await dismissCropOrConfirmDialog(page);

    for (const selector of IMAGE_PREVIEW_SELECTORS) {
      if (await page.locator(selector).first().isVisible({ timeout: 400 }).catch(() => false)) {
        await captureDebugStep(page, debug, "attach-image-preview");
        return true;
      }
    }

    await page.waitForTimeout(500);
  }

  return false;
}

async function uploadViaFileChooser(
  page: Page,
  imagePath: string,
  debug?: DebugRun | null
): Promise<boolean> {
  for (const selector of ATTACH_TRIGGER_SELECTORS) {
    const trigger = page.locator(selector).first();
    if (!(await trigger.isVisible({ timeout: 900 }).catch(() => false))) continue;

    try {
      const [fileChooser] = await Promise.all([
        page.waitForEvent("filechooser", { timeout: 6000 }),
        trigger.click(),
      ]);
      await fileChooser.setFiles(imagePath);
      await humanDelay(800, 1400);
      if (await waitForImagePreview(page, debug)) return true;
    } catch {
      // try next trigger
    }
  }

  return false;
}

async function uploadViaHiddenInput(
  page: Page,
  imagePath: string,
  debug?: DebugRun | null
): Promise<boolean> {
  const inputs = page.locator('input[type="file"]');
  const count = await inputs.count();

  for (let i = 0; i < count; i++) {
    const input = inputs.nth(i);
    try {
      await input.setInputFiles(imagePath);
      await humanDelay(800, 1400);
      if (await waitForImagePreview(page, debug)) return true;
    } catch {
      // try next input
    }
  }

  return false;
}

async function uploadViaToolbarEvaluate(
  page: Page,
  imagePath: string,
  debug?: DebugRun | null
): Promise<boolean> {
  const clicked = await page.evaluate(() => {
    const labels = ["附加", "Attach", "媒體", "Media", "照片", "Photo", "相簿", "Album"];
    for (const label of labels) {
      const node = document.querySelector(`[aria-label*="${label}"]`);
      if (node instanceof HTMLElement) {
        node.click();
        return true;
      }
    }
    return false;
  });

  if (!clicked) return false;

  try {
    const fileChooser = await page.waitForEvent("filechooser", { timeout: 5000 });
    await fileChooser.setFiles(imagePath);
    await humanDelay(800, 1400);
    return waitForImagePreview(page, debug);
  } catch {
    return uploadViaHiddenInput(page, imagePath, debug);
  }
}

async function attachMultipleImages(
  page: Page,
  imagePaths: string[],
  debug?: DebugRun | null
): Promise<boolean> {
  if (imagePaths.length <= 1) return false;

  for (const selector of ATTACH_TRIGGER_SELECTORS) {
    const trigger = page.locator(selector).first();
    if (!(await trigger.isVisible({ timeout: 900 }).catch(() => false))) continue;

    try {
      const [fileChooser] = await Promise.all([
        page.waitForEvent("filechooser", { timeout: 6000 }),
        trigger.click(),
      ]);
      await fileChooser.setFiles(imagePaths);
      await humanDelay(1200, 2000);
      if (await waitForImagePreview(page, debug, 25000)) {
        await captureDebugStep(page, debug, "attach-images-multi", { count: imagePaths.length });
        return true;
      }
    } catch {
      // try next trigger
    }
  }

  return false;
}

export async function attachImages(
  page: Page,
  imagePaths: string[],
  debug?: DebugRun | null
): Promise<boolean> {
  if (imagePaths.length === 0) return true;

  const prepared = await Promise.all(imagePaths.map((p) => prepareImageForBrowserUpload(p)));

  try {
    const paths = prepared.map((item) => item.path);

    if (paths.length > 1 && (await attachMultipleImages(page, paths, debug))) {
      return true;
    }

    for (const item of prepared) {
      const attached = await attachImage(page, item.path, debug);
      if (!attached) return false;
      await humanDelay(900, 1400);
    }

    return true;
  } finally {
    await Promise.all(prepared.map((item) => item.cleanup?.()));
  }
}

export async function attachImage(
  page: Page,
  imagePath: string,
  debug?: DebugRun | null
): Promise<boolean> {
  await captureDebugStep(page, debug, "attach-image-start", { imagePath });

  if (await uploadViaFileChooser(page, imagePath, debug)) {
    await captureDebugStep(page, debug, "attach-image-done");
    return true;
  }

  if (await uploadViaHiddenInput(page, imagePath, debug)) {
    await captureDebugStep(page, debug, "attach-image-done");
    return true;
  }

  if (await uploadViaToolbarEvaluate(page, imagePath, debug)) {
    await captureDebugStep(page, debug, "attach-image-done");
    return true;
  }

  await captureDebugStep(page, debug, "attach-image-failed");
  return false;
}