import { readFileSync } from "node:fs";
import { describe, expect, it, vi } from "vitest";
import { ChallengeRequiredError } from "../src/core/browserErrors.js";
import {
  createAndroidBrowserFetcher,
  type NativePageResult,
  type NativeWebViewFetcher,
} from "../android/androidBrowserFetcher.js";

const result = (overrides: Partial<NativePageResult> = {}): NativePageResult => ({
  storeId: "trodo",
  url: "https://www.trodo.lv/api/search",
  status: 200,
  body: "ok",
  challenged: false,
  cookieCount: 2,
  ...overrides,
});

function native(overrides: Partial<NativeWebViewFetcher> = {}): NativeWebViewFetcher {
  return {
    getDeviceLanguages: vi.fn().mockResolvedValue({ languages: ["en-US"] }),
    fetchPageHtml: vi.fn().mockResolvedValue(result({ body: "<html>ok</html>" })),
    fetchResource: vi.fn().mockResolvedValue(result()),
    show: vi.fn().mockResolvedValue({ cleared: true }),
    dismiss: vi.fn().mockResolvedValue({ dismissed: true }),
    ...overrides,
  };
}

describe("createAndroidBrowserFetcher", () => {
  it("passes JSON fetch headers through the warm same-origin resource call", async () => {
    const plugin = native();
    const browser = createAndroidBrowserFetcher(plugin);

    await expect(browser.fetchJson(
      "trodo",
      "https://www.trodo.lv/",
      "https://www.trodo.lv/api/search",
      { headers: { "X-Test": "yes" }, solvePage: "https://www.trodo.lv/search" },
    )).resolves.toBe("ok");
    expect(plugin.fetchResource).toHaveBeenCalledWith({
      storeId: "trodo",
      homepage: "https://www.trodo.lv/",
      url: "https://www.trodo.lv/api/search",
      headers: {
        Accept: "application/json",
        "X-Requested-With": "XMLHttpRequest",
        "X-Test": "yes",
      },
      solvePage: "https://www.trodo.lv/search",
      timeoutMs: 25_000,
    });
  });

  it("maps a challenged native response to the engine challenge signal", async () => {
    const fetchPageHtml = vi.fn().mockResolvedValue(result({ challenged: true, status: 403 }));
    const browser = createAndroidBrowserFetcher(native({ fetchPageHtml }));

    await expect(browser.fetchPageHtml("autodoc", "https://autodoc.lv/search"))
      .rejects.toEqual(expect.any(ChallengeRequiredError));
    expect(fetchPageHtml).toHaveBeenCalledWith({
      storeId: "autodoc",
      url: "https://autodoc.lv/search",
      timeoutMs: 42_000,
      challengeClearMs: 25_000,
    });
  });

  it("rejects non-success resource responses", async () => {
    const browser = createAndroidBrowserFetcher(native({
      fetchResource: vi.fn().mockResolvedValue(result({ status: 503 })),
    }));

    await expect(browser.fetchOriginHtml(
      "ic24",
      "https://ic24.lv/",
      "https://ic24.lv/search",
    )).rejects.toThrow("ic24: WebView fetch HTTP 503");
  });

  it("exposes safe native lifecycle diagnostics once", async () => {
    const browser = createAndroidBrowserFetcher(native({
      fetchPageHtml: vi.fn().mockResolvedValue(result({
        body: "<html>ok</html>",
        diagnostics: [
          { at: 123, event: "navigation_started" },
          { at: 456, event: "response", status: 200, detail: "complete" },
        ],
      })),
    }));

    await browser.fetchPageHtml("autodoc", "https://autodoc.lv/search");
    expect(browser.takeDiagnostics?.("autodoc")).toEqual([
      { at: 123, event: "navigation_started" },
      { at: 456, event: "response", status: 200, detail: "complete" },
    ]);
    expect(browser.takeDiagnostics?.("autodoc")).toEqual([]);
  });

  it("keeps a broken Turnstile state in the challenge flow instead of returning HTTP 503", () => {
    const source = readFileSync(
      "android/app/src/main/java/lv/carp/partscomparator/WebViewFetcherPlugin.java",
      "utf8",
    );

    expect(source).toContain("boolean actionRequired = challenged || turnstileBroken;");
    expect(source).toContain("if (!forced && actionRequired && beforeChallengeDeadline())");
    expect(source).toContain("resetOriginSession(pageRequestUrl);");
    expect(source).not.toContain('replace("; wv)", ")")');
    expect(source).not.toContain('resolve(snapshot.optString("html", ""), false, 503)');
  });
});
