import { act } from "react";
import { createRoot, type Root } from "react-dom/client";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";

const mocks = vi.hoisted(() => ({
  dispatch: vi.fn(),
  handleQrisResume: vi.fn(),
  hasHiddenQrisPayment: false,
  resetLaunchState: vi.fn(),
  payment: {
    selectedPlanId: "vip_monthly",
    autoRenew: true,
    payChannel: "stripe" as "stripe" | "ezpay",
    commercialOfferId: null,
    chatActionId: null,
    currentOrderId: null,
    errorMessage: null,
    isCreatingOrder: false,
    isPollingOrder: false,
  },
}));

vi.mock("@/stores/payment/payment-context", () => ({
  usePaymentState: () => mocks.payment,
  usePaymentDispatch: () => mocks.dispatch,
}));

vi.mock("@/app/_hooks/use-payment-launch-flow", () => ({
  usePaymentLaunchFlow: () => ({
    handleQrisResume: mocks.handleQrisResume,
    hasHiddenQrisPayment: mocks.hasHiddenQrisPayment,
    resetLaunchState: mocks.resetLaunchState,
    launch: {},
  }),
}));

vi.mock("@/app/_components/payment/payment-launch-dialogs", () => ({
  PaymentLaunchDialogs: () => null,
}));

vi.mock("@/app/_components/payment/external-browser-checkout-button", () => ({
  ExternalBrowserCheckoutButton: () => null,
}));

vi.mock("../subscription-cta-button", () => ({
  SubscriptionCtaButton: ({
    children,
    disabled,
    onClick,
  }: {
    children: React.ReactNode;
    disabled?: boolean;
    onClick?: () => void;
  }) => (
    <button type="button" disabled={disabled} onClick={onClick}>
      {children}
    </button>
  ),
}));

vi.mock("../subscription-renewal-confirmation-dialog", () => ({
  SubscriptionRenewalConfirmationDialog: ({
    open,
    onCancel,
    onConfirm,
  }: {
    open: boolean;
    onCancel: () => void;
    onConfirm: () => void;
  }) =>
    open ? (
      <div role="dialog">
        <button type="button" onClick={onCancel}>Cancel</button>
        <button type="button" onClick={onConfirm}>Confirm</button>
      </div>
    ) : null,
}));

import { SubscriptionCheckoutButton } from "../subscription-checkout-button";

const renewalPlan = {
  id: "vip_monthly",
  title: "Monthly",
  price: "19.90",
  currency: "usd",
  originalPrice: "39.90",
};

describe("SubscriptionCheckoutButton renewal confirmation", () => {
  let container: HTMLDivElement;
  let root: Root;

  beforeEach(() => {
    (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
      .IS_REACT_ACT_ENVIRONMENT = true;
    localStorage.clear();
    mocks.dispatch.mockClear();
    mocks.handleQrisResume.mockClear();
    mocks.hasHiddenQrisPayment = false;
    mocks.resetLaunchState.mockClear();
    mocks.payment.selectedPlanId = "vip_monthly";
    mocks.payment.autoRenew = true;
    mocks.payment.payChannel = "stripe";
    mocks.payment.isCreatingOrder = false;
    mocks.payment.isPollingOrder = false;
    container = document.createElement("div");
    document.body.appendChild(container);
    root = createRoot(container);
  });

  afterEach(() => {
    act(() => root.unmount());
    container.remove();
  });

  it("shows confirmation on the first auto-renewing VIP checkout and resumes after confirm", () => {
    renderButton(root, "user-1");

    act(() => clickButton(container, "Pay and Top Up"));
    expect(container.querySelector('[role="dialog"]')).not.toBeNull();
    expect(mocks.dispatch).not.toHaveBeenCalled();

    act(() => clickButton(container, "Confirm"));
    expect(container.querySelector('[role="dialog"]')).toBeNull();
    expect(mocks.resetLaunchState).toHaveBeenCalledOnce();
    expect(mocks.dispatch).toHaveBeenCalledWith({
      type: "PaymentCreateOrderSubmitted",
    });
  });

  it("does not ask the same user again after confirmation", () => {
    renderButton(root, "user-1");
    act(() => clickButton(container, "Pay and Top Up"));
    act(() => clickButton(container, "Confirm"));
    mocks.dispatch.mockClear();
    mocks.resetLaunchState.mockClear();

    act(() => root.unmount());
    root = createRoot(container);
    renderButton(root, "user-1");
    act(() => clickButton(container, "Pay and Top Up"));

    expect(container.querySelector('[role="dialog"]')).toBeNull();
    expect(mocks.resetLaunchState).toHaveBeenCalledOnce();
    expect(mocks.dispatch).toHaveBeenCalledWith({
      type: "PaymentCreateOrderSubmitted",
    });
  });

  it("keeps asking after cancel and isolates acknowledgement by user", () => {
    renderButton(root, "user-1");
    act(() => clickButton(container, "Pay and Top Up"));
    act(() => clickButton(container, "Cancel"));
    act(() => clickButton(container, "Pay and Top Up"));
    expect(container.querySelector('[role="dialog"]')).not.toBeNull();

    act(() => clickButton(container, "Confirm"));
    mocks.dispatch.mockClear();
    act(() => root.unmount());
    root = createRoot(container);
    renderButton(root, "user-2");
    act(() => clickButton(container, "Pay and Top Up"));

    expect(container.querySelector('[role="dialog"]')).not.toBeNull();
    expect(mocks.dispatch).not.toHaveBeenCalled();
  });

  it("skips confirmation for a non-renewing purchase", () => {
    mocks.payment.autoRenew = false;
    renderButton(root, "user-1", null);

    act(() => clickButton(container, "Pay and Top Up"));

    expect(container.querySelector('[role="dialog"]')).toBeNull();
    expect(mocks.dispatch).toHaveBeenCalledWith({
      type: "PaymentCreateOrderSubmitted",
    });
  });

  it("skips confirmation for one-time EzPay membership checkout", () => {
    mocks.payment.payChannel = "ezpay";
    renderButton(root, "user-1");

    act(() => clickButton(container, "Pay and Top Up"));

    expect(container.querySelector('[role="dialog"]')).toBeNull();
    expect(mocks.dispatch).toHaveBeenCalledWith({
      type: "PaymentCreateOrderSubmitted",
    });
  });

  it("reopens a hidden QRIS order without creating another order", () => {
    mocks.payment.payChannel = "ezpay";
    mocks.payment.isPollingOrder = true;
    mocks.hasHiddenQrisPayment = true;

    act(() =>
      root.render(
        <SubscriptionCheckoutButton
          disabled
          subscriptionType="topup"
          sourceCharacterSlug="elio"
        />,
      ),
    );

    const resumeButton = getButton(container, "Resume QRIS payment");
    expect(resumeButton.disabled).toBe(false);
    act(() => resumeButton.click());

    expect(mocks.handleQrisResume).toHaveBeenCalledOnce();
    expect(mocks.resetLaunchState).not.toHaveBeenCalled();
    expect(mocks.dispatch).not.toHaveBeenCalled();
  });
});

function renderButton(
  root: Root,
  renewalConsentSubjectId: string,
  plan: typeof renewalPlan | null = renewalPlan,
): void {
  act(() =>
    root.render(
      <SubscriptionCheckoutButton
        subscriptionType="vip"
        sourceCharacterSlug="elio"
        renewalPlan={plan}
        renewalConsentSubjectId={renewalConsentSubjectId}
      />,
    ),
  );
}

function clickButton(root: ParentNode, label: string): void {
  getButton(root, label).click();
}

function getButton(root: ParentNode, label: string): HTMLButtonElement {
  const button = Array.from(root.querySelectorAll("button")).find(
    (item) => item.textContent?.trim() === label,
  );
  if (!button) throw new Error(`Expected ${label} button`);
  return button;
}
