import { render, screen, waitFor } from '@testing-library/react';
import { http, HttpResponse } from 'msw';
import { describe, expect, it } from 'vitest';

import { server } from '@/test/setup';

import { SwapConfirmationDialog } from './SwapConfirmationDialog';

const baseTiers = {
  pro: {
    name: 'Professional',
    features: ['Unlimited recommendations', '5 sites'],
    limits: { max_sites_per_user: 5, max_drafts_per_month: 50, seats: 1 },
    price: 29,
    price_annual: null,
  },
  team: {
    name: 'Team',
    features: ['Unlimited recommendations', '20 sites', 'Team seats'],
    limits: { max_sites_per_user: 20, max_drafts_per_month: 200, seats: 5 },
    price: 79,
    price_annual: null,
  },
};

interface PreviewFixture {
  current_plan: string;
  new_plan: string;
  current_price: number;
  new_price: number;
  proration_amount: number | null;
  proration_is_estimate: boolean;
  cadence_change: boolean;
  current_billing_interval: string;
  new_billing_interval: string;
  is_upgrade: boolean;
  warnings: string[];
  limit_comparison: unknown[];
  limit_violations: string[];
}

const basePreview: PreviewFixture = {
  current_plan: 'Professional',
  new_plan: 'Team',
  current_price: 2900,
  new_price: 7900,
  proration_amount: 5000,
  proration_is_estimate: true,
  cadence_change: false,
  current_billing_interval: 'monthly',
  new_billing_interval: 'monthly',
  is_upgrade: true,
  warnings: [],
  limit_comparison: [],
  limit_violations: [],
};

function renderDialog(overrides: Partial<PreviewFixture> = {}) {
  const preview: PreviewFixture = { ...basePreview, ...overrides };
  server.use(http.get('/billing/preview-swap', () => HttpResponse.json(preview)));

  return render(
    <SwapConfirmationDialog
      open={true}
      onOpenChange={() => undefined}
      currentPlan="pro"
      newPlan="team"
      newPriceId="price_team_monthly"
      tiers={baseTiers}
    />,
  );
}

describe('SwapConfirmationDialog — PAY-005 proration estimate caveat', () => {
  it('shows the estimate notice when proration_is_estimate is true (non-cadence)', async () => {
    renderDialog({ proration_is_estimate: true, cadence_change: false });

    await waitFor(() => {
      expect(screen.getByTestId('proration-estimate-notice')).toBeInTheDocument();
    });

    const notice = screen.getByTestId('proration-estimate-notice');
    expect(notice).toHaveTextContent('Estimated adjustment');
    expect(notice).toHaveTextContent('your actual invoice may differ');
    // CODEX-003: must NOT claim this IS the invoice amount
    expect(notice).not.toHaveTextContent('final amount on your next invoice');
  });

  it('shows the cadence-change copy when cadence_change is true', async () => {
    renderDialog({
      proration_is_estimate: true,
      cadence_change: true,
      current_billing_interval: 'monthly',
      new_billing_interval: 'annual',
    });

    await waitFor(() => {
      expect(screen.getByTestId('proration-estimate-notice')).toBeInTheDocument();
    });

    const notice = screen.getByTestId('proration-estimate-notice');
    expect(notice).toHaveTextContent('Estimated adjustment');
    expect(notice).toHaveTextContent('switching from');
    expect(notice).toHaveTextContent('annual');
    expect(notice).not.toHaveTextContent('final amount on your next invoice');
  });

  it('does not show the estimate notice when proration_amount is null', async () => {
    renderDialog({ proration_amount: null, proration_is_estimate: true });

    await waitFor(() => {
      // Confirm preview loaded (loading spinner gone)
      expect(screen.queryByText('Loading comparison…')).not.toBeInTheDocument();
    });

    expect(screen.queryByTestId('proration-estimate-notice')).not.toBeInTheDocument();
  });

  it('renders the formatted proration amount in dollars (cents conversion)', async () => {
    renderDialog({ proration_amount: 5000 });

    await waitFor(() => {
      // 5000 cents = $50.00; label is "Prorated charge"
      expect(screen.getByText('$50.00')).toBeInTheDocument();
      expect(screen.getByText('Prorated charge')).toBeInTheDocument();
    });
  });

  // CODEX-001: negative proration (downgrade credit) must NOT say "Prorated charge"
  it('labels a negative proration amount as "Prorated credit" and shows the absolute value', async () => {
    renderDialog({ proration_amount: -5000, is_upgrade: false });

    await waitFor(() => {
      expect(screen.getByText('Prorated credit')).toBeInTheDocument();
      expect(screen.getByText('$50.00')).toBeInTheDocument();
    });

    expect(screen.queryByText('Prorated charge')).not.toBeInTheDocument();
    expect(screen.queryByText('-$50.00')).not.toBeInTheDocument();
  });

  // CODEX-002: zero proration should not render the row at all
  it('hides the proration row when proration_amount is zero', async () => {
    renderDialog({ proration_amount: 0 });

    await waitFor(() => {
      expect(screen.queryByText('Loading comparison…')).not.toBeInTheDocument();
    });

    expect(screen.queryByText('Prorated charge')).not.toBeInTheDocument();
    expect(screen.queryByText('Prorated credit')).not.toBeInTheDocument();
    expect(screen.queryByTestId('proration-estimate-notice')).not.toBeInTheDocument();
  });
});
