import { describe, expect, it, vi, beforeEach } from "vitest";
import type { Page } from "playwright";
import type { QuoteRequest } from "@/modules/providers/quote-provider";
import { fillAddressStep } from "@/workers/rpa/kernel/steps/address-step";
import type { RPAContext } from "@/workers/rpa/kernel/context";

const mockSelectAddress = vi.fn();
const mockWaitForCommit = vi.fn();
const mockGetDiagnostics = vi.fn();
const mockGetCommitState = vi.fn();

vi.mock("@/workers/rpa/address-adapter", () => ({
  createAddressAdapter: vi.fn(() => ({
    selectAddress: mockSelectAddress,
    waitForCommit: mockWaitForCommit,
    getCommittedAddress: vi.fn(),
    getDiagnostics: mockGetDiagnostics,
    getCommitState: mockGetCommitState,
  })),
  formatDiagnosticsFailure: vi.fn((d) => d.status ?? "failed"),
}));

vi.mock("@/lib/rpa/address-mode", () => ({
  isRpaAddressMockMode: vi.fn().mockReturnValue(false),
  getRpaAddressMode: vi.fn().mockReturnValue("real"),
}));

vi.mock("@/workers/rpa/address-commit", async (importOriginal) => {
  const actual = await importOriginal<typeof import("@/workers/rpa/address-commit")>();
  return {
    ...actual,
    blurAddressEditing: vi.fn().mockResolvedValue(undefined),
    dismissAddressSuggestionDropdown: vi.fn().mockResolvedValue(undefined),
    settlePickupBeforeDelivery: vi.fn().mockResolvedValue(undefined),
    assertDualAddressStable: vi.fn().mockResolvedValue(undefined),
    ensureDualAddressCommitted: vi.fn().mockResolvedValue(undefined),
    isPickupReadyForDeliveryInput: vi.fn().mockResolvedValue(true),
    lockPickupIntoChip: vi.fn().mockResolvedValue(undefined),
  };
});

vi.mock("@/workers/rpa/address-suggestions", () => ({
  isAddressSuggestionDropdownOpen: vi.fn().mockResolvedValue(false),
  getAddressConfirmationSurfaceText: vi.fn().mockResolvedValue(""),
}));

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

vi.mock("@/workers/rpa/cargo-lock", () => ({
  settleAddressFieldsForCargo: vi.fn().mockResolvedValue(undefined),
  assertBothAddressesLocked: vi.fn().mockResolvedValue(undefined),
}));

vi.mock("@/workers/rpa/kernel/step-guard", () => ({
  runKernelSubStep: vi.fn(
    (_step: string, _label: string, fn: () => Promise<unknown>) => fn(),
  ),
}));

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

import { openAddressSide } from "@/workers/rpa/address-side";

function baseRequest(overrides?: Partial<QuoteRequest>): QuoteRequest {
  return {
    cargoHash: "hash",
    quoteSessionId: undefined,
    pickup: {
      street: "1234 Warehouse Street",
      city: "Farmers Branch",
      state: "TX",
      zip: "75234",
      placeId: "p1",
      formattedAddress: "1234 Warehouse Street Farmers Branch TX",
      selectedFromSuggestions: true,
      mothershipOptionId: "o1",
      mothershipDisplayLabel: "1234 Warehouse Road Farmers Branch TX",
      selectedFromMothership: true,
    },
    delivery: {
      street: "5678 Distribution Way",
      city: "Fort Worth",
      state: "TX",
      zip: "76106",
      placeId: "d1",
      formattedAddress: "5678 Distribution Way Fort Worth TX",
      selectedFromSuggestions: true,
      mothershipOptionId: "o2",
      mothershipDisplayLabel: "5678 Distribution Drive Fort Worth TX",
      selectedFromMothership: true,
    },
    weightLb: 500,
    dimsIn: { l: 48, w: 40, h: 48 },
    palletCount: 2,
    cargoType: "general_freight",
    ...overrides,
  };
}

function mockCtx(widgetText = ""): RPAContext {
  const page = {
    keyboard: { press: vi.fn().mockResolvedValue(undefined) },
    waitForTimeout: vi.fn().mockResolvedValue(undefined),
    locator: vi.fn().mockReturnValue({
      innerText: vi.fn().mockResolvedValue(widgetText),
    }),
    url: vi.fn().mockReturnValue("https://example.com"),
  } as unknown as Page;

  return {
    page,
    selectors: {
      resolve: vi.fn().mockReturnValue("#widget"),
      specs: vi.fn().mockReturnValue([]),
      widget: vi.fn(),
    },
    exec: { evaluateOnLocator: vi.fn() },
    runtime: { evaluateOnLocator: vi.fn() },
    getWidgetScrollAt: vi.fn().mockReturnValue(0),
    setWidgetScrollAt: vi.fn(),
    state: {
      markAddressSide: vi.fn(),
      settleAddressEditing: vi.fn().mockResolvedValue(undefined),
      axelPlaceLocations: { pickup: { placeId: "ChIJ_test" } },
    },
  } as unknown as RPAContext;
}

describe("fillAddressStep", () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockSelectAddress.mockImplementation(async (params) => {
      params.ctx.state.axelPlaceLocations[params.side] = {
        placeId: "ChIJ_test",
      };
      return {
        success: true,
        placeId: "ChIJ_test",
        formattedAddress: "test",
        committed: true,
      };
    });
    mockWaitForCommit.mockResolvedValue({
      success: true,
      placeId: "ChIJ_test",
      formattedAddress: "test",
      committed: true,
    });
    mockGetCommitState.mockReturnValue("COMMITTED");
    mockGetDiagnostics.mockReturnValue({
      address: "test",
      placeId: "ChIJ_test",
      method: "mothership",
      events: [],
      placeRequestObserved: true,
      status: "ok",
      commitState: "COMMITTED",
    });
  });

  it("有 quote_session 时仍走完整填表（禁止跳过）", async () => {
    const ctx = mockCtx(
      "1234 Warehouse Road Farmers Branch TX 5678 Distribution Drive Fort Worth TX",
    );
    const loc = {
      isEditable: vi.fn().mockResolvedValue(true),
      waitFor: vi.fn().mockResolvedValue(undefined),
      click: vi.fn().mockResolvedValue(undefined),
    };
    const { visibleStreetInput } = await import("@/workers/rpa/address-side");
    vi.mocked(visibleStreetInput).mockResolvedValue(loc as never);

    await fillAddressStep(
      ctx,
      baseRequest({ quoteSessionId: "session-uuid" }),
    );

    expect(openAddressSide).toHaveBeenCalled();
    expect(mockSelectAddress).toHaveBeenCalled();
  });

  it("无 quote_session 时走完整填表", async () => {
    const ctx = mockCtx(
      "1234 Warehouse Road Farmers Branch TX 5678 Distribution Drive Fort Worth TX",
    );
    const loc = {
      isEditable: vi.fn().mockResolvedValue(true),
      waitFor: vi.fn().mockResolvedValue(undefined),
      click: vi.fn().mockResolvedValue(undefined),
    };
    const { visibleStreetInput } = await import("@/workers/rpa/address-side");
    vi.mocked(visibleStreetInput).mockResolvedValue(loc as never);

    await fillAddressStep(ctx, baseRequest());

    expect(openAddressSide).toHaveBeenCalled();
    expect(mockSelectAddress).toHaveBeenCalledTimes(2);
  });

  it("widget 未体现确认地址时抛错", async () => {
    const { assertDualAddressStable } = await import(
      "@/workers/rpa/address-commit"
    );
    vi.mocked(assertDualAddressStable).mockRejectedValueOnce({
      code: "ADDRESS_NOT_CONFIRMED",
    });
    const ctx = mockCtx("999 Wrong Street Fort Worth TX");
    const loc = {
      isEditable: vi.fn().mockResolvedValue(true),
      waitFor: vi.fn().mockResolvedValue(undefined),
      click: vi.fn().mockResolvedValue(undefined),
    };
    const { visibleStreetInput } = await import("@/workers/rpa/address-side");
    vi.mocked(visibleStreetInput).mockResolvedValue(loc as never);

    await expect(fillAddressStep(ctx, baseRequest())).rejects.toMatchObject({
      code: "ADDRESS_NOT_CONFIRMED",
    });
  });
});
