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

import type { PayChannel } from "@/data/schemas/payment";
import type { PaymentMethodConfig } from "@/lib/payment/payment_method";

import { usePaymentMethodSelection } from "../use-payment-method-selection";

describe("usePaymentMethodSelection", () => {
  let container: HTMLDivElement;
  let root: Root;

  beforeEach(() => {
    (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean })
      .IS_REACT_ACT_ENVIRONMENT = true;
    container = document.createElement("div");
    document.body.appendChild(container);
    root = createRoot(container);
  });

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

  it("forces Stripe when payment method selection is unavailable", () => {
    const onChange = vi.fn();
    renderHarness(root, {
      config: {
        canChoosePaymentMethod: false,
        initialPayChannel: "stripe",
        showPaymentMethodSelector: false,
      },
      currentPayChannel: "ezpay",
      onChange,
    });

    expect(onChange).toHaveBeenCalledWith("stripe");
  });

  it("applies the default channel once when no return channel was requested", () => {
    const onChange = vi.fn();
    const config = {
      canChoosePaymentMethod: true,
      initialPayChannel: "ezpay",
      showPaymentMethodSelector: true,
    } as const;
    renderHarness(root, {
      config,
      currentPayChannel: "stripe",
      onChange,
    });
    renderHarness(root, {
      config,
      currentPayChannel: "ezpay",
      onChange,
    });

    expect(onChange).toHaveBeenCalledTimes(1);
    expect(onChange).toHaveBeenCalledWith("ezpay");
  });

  it("preserves an explicit return channel and pauses while payment is busy", () => {
    const onChange = vi.fn();
    renderHarness(root, {
      config: {
        canChoosePaymentMethod: true,
        initialPayChannel: "ezpay",
        showPaymentMethodSelector: true,
      },
      currentPayChannel: "stripe",
      requestedPayChannel: "stripe",
      onChange,
    });
    renderHarness(root, {
      config: {
        canChoosePaymentMethod: false,
        initialPayChannel: "stripe",
        showPaymentMethodSelector: false,
      },
      currentPayChannel: "ezpay",
      isPaymentBusy: true,
      onChange,
    });

    expect(onChange).not.toHaveBeenCalled();
  });
});

interface HarnessProps {
  config: PaymentMethodConfig;
  currentPayChannel: PayChannel;
  requestedPayChannel?: PayChannel | null;
  isPaymentBusy?: boolean;
  onChange: (payChannel: PayChannel) => void;
}

function renderHarness(root: Root, props: HarnessProps): void {
  act(() => {
    root.render(<Harness {...props} />);
  });
}

function Harness({
  config,
  currentPayChannel,
  requestedPayChannel = null,
  isPaymentBusy = false,
  onChange,
}: HarnessProps) {
  usePaymentMethodSelection({
    config,
    currentPayChannel,
    requestedPayChannel,
    isPaymentBusy,
    onChange,
  });
  return null;
}
