import { describe, expect, it, vi, beforeEach, afterEach } from "vitest";
import type { Page } from "playwright";
import { RPA_SESSION_WIDGET_TIMEOUT_MS } from "@/lib/constants/rpa";
import { MothershipQuotePageAdapter } from "@/workers/rpa/quote-page-adapter";
import { RpaError } from "@/modules/rpa/errors";
import { waitForQuoteWidgetReady } from "@/workers/rpa/address-side";

vi.mock("@/workers/rpa/page-prep", () => ({
  stabilizeQuoteLandingPage: vi.fn().mockResolvedValue(undefined),
}));

vi.mock("@/workers/rpa/address-side", () => ({
  waitForQuoteWidgetReady: vi.fn().mockResolvedValue(undefined),
  ensureQuoteFormVisible: vi.fn().mockResolvedValue(undefined),
}));

function mockPage(overrides: {
  url?: string;
  goto?: ReturnType<typeof vi.fn>;
  pickupVisible?: boolean;
  submitVisible?: boolean;
  bodyText?: string;
  cfCount?: number;
}): Page {
  const url = overrides.url ?? "https://quote.example.com/";
  const bodyLocator = {
    innerText: vi.fn().mockResolvedValue(overrides.bodyText ?? "Quote form"),
  };
  return {
    url: () => url,
    goto: overrides.goto ?? vi.fn().mockResolvedValue(undefined),
    waitForLoadState: vi.fn().mockResolvedValue(undefined),
    locator: vi.fn((selector: string) => {
      if (selector === "body") {
        return bodyLocator;
      }
      return {
        first: () => ({
          isVisible: vi.fn().mockImplementation(async () => {
            if (selector.includes("pickup")) {
              return overrides.pickupVisible ?? true;
            }
            if (selector.includes("submit")) {
              return overrides.submitVisible ?? true;
            }
            return false;
          }),
          innerText: vi.fn().mockResolvedValue(overrides.bodyText ?? "Quote form"),
        }),
        count: vi.fn().mockResolvedValue(overrides.cfCount ?? 0),
      };
    }),
  } as unknown as Page;
}

describe("MothershipQuotePageAdapter", () => {
  const adapter = new MothershipQuotePageAdapter();
  const envBackup = { ...process.env };

  beforeEach(() => {
    process.env.MOTHERSHIP_EMAIL = "";
    process.env.MOTHERSHIP_PASSWORD = "";
    process.env.RPA_SELECTOR_PICKUP_STREET = '[data-testid="pickup"]';
    process.env.RPA_SELECTOR_SUBMIT = 'button[type="submit"]';
  });

  afterEach(() => {
    process.env = { ...envBackup };
  });

  it("isLoginWithoutCredentials：login 且无凭据", () => {
    const page = mockPage({ url: "https://example.com/login" });
    expect(adapter.isLoginWithoutCredentials(page)).toBe(true);
  });

  it("resolveQuoteEntry：首个 URL 通过 preCheck", async () => {
    process.env.MOTHERSHIP_QUOTE_URLS =
      "https://good.example.com,https://bad.example.com";
    process.env.RPA_SELECTOR_PICKUP_SECTION = "text=pickup";
    const page = mockPage({
      pickupVisible: true,
      submitVisible: false,
    });

    const result = await adapter.resolveQuoteEntry(page);
    expect(result.activeUrl).toBe("https://good.example.com");
    expect(page.goto).toHaveBeenCalledTimes(1);
  });

  it("resolveQuoteEntry：全部失败 → QUOTE_ENTRY_UNAVAILABLE", async () => {
    process.env.MOTHERSHIP_QUOTE_URLS =
      "https://bad1.example.com,https://bad2.example.com";
    const page = mockPage({
      pickupVisible: false,
      submitVisible: false,
    });
    vi.mocked(page.goto).mockRejectedValue(new Error("net err"));

    await expect(adapter.resolveQuoteEntry(page)).rejects.toMatchObject({
      code: "QUOTE_ENTRY_UNAVAILABLE",
    });
    expect(page.goto).toHaveBeenCalled();
  });
});

describe("MothershipQuotePageAdapter preCheck", () => {
  const adapter = new MothershipQuotePageAdapter();

  beforeEach(() => {
    process.env.RPA_SELECTOR_PICKUP_STREET = '[data-testid="pickup"]';
    process.env.RPA_SELECTOR_SUBMIT = 'button[type="submit"]';
  });

  it("login 无凭据 → STRUCT_CHANGE", async () => {
    process.env.MOTHERSHIP_EMAIL = "";
    const page = mockPage({ url: "https://example.com/login" });
    await expect(adapter.preCheck(page)).rejects.toMatchObject({
      code: "STRUCT_CHANGE",
    });
  });

  it("reuseSession 时使用较短 widget 等待", async () => {
    const page = mockPage({ pickupVisible: true });
    await adapter.preCheck(page, { reuseSession: true });
    expect(waitForQuoteWidgetReady).toHaveBeenCalledWith(
      page,
      RPA_SESSION_WIDGET_TIMEOUT_MS,
    );
  });
});
