import React from 'react';
import { describe, expect, it, vi, beforeEach } from 'vitest';
import { act, fireEvent, render, screen, waitFor } from '@testing-library/react';
import {
  StripeCheckoutForm,
  PayPalCheckout,
} from '../pages/WelcomeOrderSummaryPage';

const stripeRef: { current: any } = { current: null };
const elementsRef: { current: any } = { current: null };
const paypalPropsRef: { current: any } = { current: null };

const {
  confirmPaymentMock,
  completePurchaseMock,
  createPayPalOrderMock,
  capturePayPalOrderMock,
} = vi.hoisted(() => ({
  confirmPaymentMock: vi.fn(),
  completePurchaseMock: vi.fn(),
  createPayPalOrderMock: vi.fn(),
  capturePayPalOrderMock: vi.fn(),
}));

vi.mock('@stripe/react-stripe-js', () => ({
  useStripe: () => stripeRef.current,
  useElements: () => elementsRef.current,
  PaymentElement: () => <div data-testid="stripe-payment-element" />,
  Elements: ({ children }: { children: React.ReactNode }) => <>{children}</>,
}));

vi.mock('@paypal/react-paypal-js', () => ({
  PayPalScriptProvider: ({ children }: { children: React.ReactNode }) => <>{children}</>,
  PayPalButtons: (props: any) => {
    paypalPropsRef.current = props;
    return <button type="button" data-testid="paypal-button">PayPal</button>;
  },
}));

vi.mock('../../api', () => ({
  completeTenantPackagePurchase: completePurchaseMock,
  createTenantPackagePaymentIntent: vi.fn(),
  assignFreeTenantPackage: vi.fn(),
  createTenantPayPalOrder: createPayPalOrderMock,
  captureTenantPayPalOrder: capturePayPalOrderMock,
}));

describe('StripeCheckoutForm', () => {
  beforeEach(() => {
    confirmPaymentMock.mockReset();
    completePurchaseMock.mockReset();
    stripeRef.current = { confirmPayment: confirmPaymentMock };
    elementsRef.current = {};
  });

  const renderStripeForm = (overrides?: Partial<React.ComponentProps<typeof StripeCheckoutForm>>) =>
    render(
      <StripeCheckoutForm
        clientSecret="secret"
        packageId={42}
        onSuccess={vi.fn()}
        t={(key: string) => key}
        {...overrides}
      />
    );

  it('completes the purchase when Stripe reports a successful payment', async () => {
    const onSuccess = vi.fn();
    confirmPaymentMock.mockResolvedValue({
      error: null,
      paymentIntent: { payment_method: 'pm_123' },
    });
    completePurchaseMock.mockResolvedValue(undefined);

    const { container } = renderStripeForm({ onSuccess });
    const form = container.querySelector('form');
    expect(form).toBeTruthy();
    fireEvent.submit(form!);

    await waitFor(() => {
      expect(completePurchaseMock).toHaveBeenCalledWith({
        packageId: 42,
        paymentMethodId: 'pm_123',
      });
    });
    expect(onSuccess).toHaveBeenCalled();
  });

  it('shows Stripe errors returned by confirmPayment', async () => {
    confirmPaymentMock.mockResolvedValue({
      error: { message: 'Card declined' },
    });

    const { container } = renderStripeForm();
    fireEvent.submit(container.querySelector('form')!);

    await waitFor(() => {
      expect(screen.getByText('Card declined')).toBeInTheDocument();
    });
    expect(completePurchaseMock).not.toHaveBeenCalled();
  });

  it('reports missing payment method id', async () => {
    confirmPaymentMock.mockResolvedValue({
      error: null,
      paymentIntent: {},
    });

    const { container } = renderStripeForm();
    fireEvent.submit(container.querySelector('form')!);

    await waitFor(() => {
      expect(screen.getByText('summary.stripe.missingPaymentId')).toBeInTheDocument();
    });
    expect(completePurchaseMock).not.toHaveBeenCalled();
  });
});

describe('PayPalCheckout', () => {
  beforeEach(() => {
    paypalPropsRef.current = null;
    createPayPalOrderMock.mockReset();
    capturePayPalOrderMock.mockReset();
  });

  it('creates and captures a PayPal order successfully', async () => {
    createPayPalOrderMock.mockResolvedValue('ORDER-123');
    capturePayPalOrderMock.mockResolvedValue(undefined);
    const onSuccess = vi.fn();

    render(
      <PayPalCheckout
        packageId={99}
        onSuccess={onSuccess}
        t={(key: string) => key}
      />
    );

    expect(paypalPropsRef.current).toBeTruthy();
    const { createOrder, onApprove } = paypalPropsRef.current;
    await act(async () => {
      const orderId = await createOrder();
      expect(orderId).toBe('ORDER-123');
    });
    await act(async () => {
      await onApprove({ orderID: 'ORDER-123' });
    });

    await waitFor(() => {
      expect(createPayPalOrderMock).toHaveBeenCalledWith(99);
      expect(capturePayPalOrderMock).toHaveBeenCalledWith('ORDER-123');
      expect(onSuccess).toHaveBeenCalled();
    });
  });

  it('surfaces missing order id errors', async () => {
    createPayPalOrderMock.mockResolvedValue('ORDER-123');
    render(
      <PayPalCheckout
        packageId={99}
        onSuccess={vi.fn()}
        t={(key: string) => key}
      />
    );

    const { onApprove } = paypalPropsRef.current;
    await act(async () => {
      await onApprove({ orderID: undefined });
    });

    await waitFor(() => {
      expect(screen.getByText('summary.paypal.missingOrderId')).toBeInTheDocument();
    });
    expect(capturePayPalOrderMock).not.toHaveBeenCalled();
  });
});
