import { fireEvent, render, screen } from '@testing-library/react';
import { beforeEach, describe, expect, it, vi } from 'vitest';

import { usePage } from '@inertiajs/react';

import { UpgradePrompt } from './UpgradePrompt';

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    usePage: vi.fn(),
    Link: ({ children, href, ...props }: { children: React.ReactNode; href: string }) => (
      <a href={href} {...props}>
        {children}
      </a>
    ),
  };
});

// Mock localStorage
const localStorageMock = (() => {
  let store: Record<string, string> = {};

  return {
    getItem: (key: string) => store[key] || null,
    setItem: (key: string, value: string) => {
      store[key] = value.toString();
    },
    removeItem: (key: string) => {
      delete store[key];
    },
    clear: () => {
      store = {};
    },
  };
})();

Object.defineProperty(window, 'localStorage', {
  value: localStorageMock,
});

describe('UpgradePrompt', () => {
  beforeEach(() => {
    localStorageMock.clear();

    // Mock usePage to return features
    vi.mocked(usePage).mockReturnValue({
      props: {
        features: {
          billing: true,
          socialAuth: false,
          emailVerification: false,
          apiTokens: false,
          userSettings: false,
          notifications: false,
          onboarding: false,
          apiDocs: false,
          twoFactor: false,
          webhooks: false,
          admin: false,
        },
        auth: { user: null },
        errors: {},
        flash: {},
      },
      url: 'http://localhost/test',
      component: 'Test',
      version: null,
      rememberedState: {},
    } as unknown as ReturnType<typeof usePage>);
  });

  it('renders when at limit', () => {
    render(
      <UpgradePrompt
        limitType="sites"
        currentUsage={1}
        maxUsage={1}
        proLimit={10}
        dismissible={true}
      />,
    );

    expect(screen.getByText(/reached your free tier limit/i)).toBeInTheDocument();
  });

  it('shows next-tier CTA for Pro users approaching Team limits', () => {
    render(
      <UpgradePrompt
        limitType="sites"
        currentUsage={4}
        maxUsage={5}
        proLimit={20}
        nextTier="Team"
        currentTierLabel="Pro plan"
      />,
    );

    expect(screen.getByRole('button', { name: /manage more sites with team/i })).toBeInTheDocument();
    expect(screen.getByText(/in your pro plan/i)).toBeInTheDocument();
  });

  it('renders when near limit (80%)', () => {
    render(
      <UpgradePrompt
        limitType="drafts"
        currentUsage={8}
        maxUsage={10}
        proLimit={100}
        dismissible={true}
      />,
    );

    expect(screen.getByText(/using 8 of 10/i)).toBeInTheDocument();
  });

  it('does not render when below 80% threshold', () => {
    const { container } = render(
      <UpgradePrompt
        limitType="sites"
        currentUsage={1}
        maxUsage={5}
        proLimit={10}
        dismissible={true}
      />,
    );

    expect(container.firstChild).toBeNull();
  });

  it('can be dismissed when near limit (not at limit)', () => {
    const { container } = render(
      <UpgradePrompt
        limitType="drafts"
        currentUsage={8}
        maxUsage={10}
        proLimit={100}
        dismissible={true}
      />,
    );

    const dismissButton = screen.getByLabelText('Dismiss');
    fireEvent.click(dismissButton);

    expect(container.firstChild).toBeNull();
  });

  it('cannot be dismissed when at limit (CRO-005)', () => {
    render(
      <UpgradePrompt
        limitType="sites"
        currentUsage={1}
        maxUsage={1}
        proLimit={10}
        dismissible={true}
      />,
    );

    // At-limit prompts should not have a dismiss button
    expect(screen.queryByLabelText('Dismiss')).not.toBeInTheDocument();
    // But the prompt itself should still be visible (default tier label is 'free tier')
    expect(screen.getByText(/reached your free tier limit/i)).toBeInTheDocument();
  });

  it('persists dismissal to localStorage when near limit', () => {
    render(
      <UpgradePrompt
        limitType="drafts"
        currentUsage={8}
        maxUsage={10}
        proLimit={100}
        dismissible={true}
      />,
    );

    const dismissButton = screen.getByLabelText('Dismiss');
    fireEvent.click(dismissButton);

    // Component stores a timestamp (not 'true') for time-based expiry
    const stored = localStorage.getItem('upgrade-prompt-dismissed-drafts-near-limit');
    expect(stored).not.toBeNull();
    expect(Number(stored)).toBeGreaterThan(0);
  });

  it('does not show dismiss button when dismissible is false', () => {
    render(
      <UpgradePrompt
        limitType="sites"
        currentUsage={1}
        maxUsage={1}
        proLimit={10}
        dismissible={false}
      />,
    );

    expect(screen.queryByLabelText('Dismiss')).not.toBeInTheDocument();
  });

  it('uses custom message when provided', () => {
    render(
      <UpgradePrompt
        limitType="sites"
        currentUsage={1}
        maxUsage={1}
        proLimit={10}
        customMessage="Custom limit message"
      />,
    );

    expect(screen.getByText('Custom limit message')).toBeInTheDocument();
  });

  it('shows correct plural forms', () => {
    render(<UpgradePrompt limitType="drafts" currentUsage={3} maxUsage={3} proLimit={100} />);

    expect(screen.getByText(/3 AI drafts/i)).toBeInTheDocument();
  });

  it('shows correct singular forms', () => {
    render(<UpgradePrompt limitType="sites" currentUsage={1} maxUsage={1} proLimit={10} />);

    expect(screen.getByText(/1 site/i)).toBeInTheDocument();
  });

  describe('PQL personalized banner (leadScoreTier=pql)', () => {
    it('shows high-intent PQL banner when leadScoreTier is pql', () => {
      render(
        <UpgradePrompt
          limitType="drafts"
          currentUsage={8}
          maxUsage={10}
          proLimit={100}
          leadScoreTier="pql"
        />,
      );

      // PQL banner uses "hitting limits" headline rather than generic message
      expect(screen.getByText(/hitting limits/i)).toBeInTheDocument();
    });

    it('shows both upgrade and "see personalised plan" CTAs in PQL banner', () => {
      render(
        <UpgradePrompt
          limitType="sites"
          currentUsage={1}
          maxUsage={1}
          proLimit={10}
          leadScoreTier="pql"
        />,
      );

      expect(screen.getByRole('button', { name: /upgrade to pro/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /see personalised plan/i })).toBeInTheDocument();
    });

    it('shows amber momentum headline (not generic upgrade message) in PQL banner', () => {
      render(
        <UpgradePrompt
          limitType="sites"
          currentUsage={1}
          maxUsage={1}
          proLimit={10}
          leadScoreTier="pql"
        />,
      );

      // PQL headline must be present — this distinguishes the PQL variant from generic
      expect(screen.getByText(/hitting limits — that means it's working/i)).toBeInTheDocument();
      // The generic primary CTA text ("Manage More Sites with Pro") should NOT be the upgrade button
      // — the PQL variant has "Upgrade to Pro" instead
      expect(screen.queryByText(/manage more sites with pro/i)).not.toBeInTheDocument();
    });

    it('falls back to generic banner when leadScoreTier is hot (not pql)', () => {
      render(
        <UpgradePrompt
          limitType="drafts"
          currentUsage={8}
          maxUsage={10}
          proLimit={100}
          leadScoreTier="hot"
        />,
      );

      // Generic banner should show
      expect(screen.getByText(/using 8 of 10/i)).toBeInTheDocument();
      // PQL headline should NOT show
      expect(screen.queryByText(/hitting limits/i)).not.toBeInTheDocument();
    });

    it('shows generic banner when leadScoreTier prop is omitted', () => {
      render(
        <UpgradePrompt
          limitType="drafts"
          currentUsage={8}
          maxUsage={10}
          proLimit={100}
        />,
      );

      expect(screen.getByText(/using 8 of 10/i)).toBeInTheDocument();
      expect(screen.queryByText(/hitting limits/i)).not.toBeInTheDocument();
    });
  });

  it("handles string proLimit (e.g., 'unlimited')", () => {
    render(
      <UpgradePrompt limitType="pages" currentUsage={50} maxUsage={50} proLimit="unlimited" />,
    );

    expect(screen.getByText(/unlimited/i)).toBeInTheDocument();
  });

  it('renders pricing link when billing is enabled', () => {
    render(<UpgradePrompt limitType="sites" currentUsage={1} maxUsage={1} proLimit={10} />);

    expect(screen.getByRole('link', { name: /manage more sites with pro/i })).toHaveAttribute(
      'href',
      '/billing/plans',
    );
  });

  it('stays dismissed after re-render when near limit', () => {
    // Set as already dismissed (store a recent timestamp — component checks time-based expiry)
    localStorage.setItem('upgrade-prompt-dismissed-drafts-near-limit', String(Date.now()));

    const { container } = render(
      <UpgradePrompt
        limitType="drafts"
        currentUsage={8}
        maxUsage={10}
        proLimit={100}
        dismissible={true}
      />,
    );

    expect(container.firstChild).toBeNull();
  });

  it('ignores localStorage dismissal when at limit (CRO-005)', () => {
    // Even with a stored dismissal, at-limit should always show
    localStorage.setItem('upgrade-prompt-dismissed-sites-at-limit', String(Date.now()));

    render(
      <UpgradePrompt
        limitType="sites"
        currentUsage={1}
        maxUsage={1}
        proLimit={10}
        dismissible={true}
      />,
    );

    // Should still be visible despite localStorage dismissal
    expect(screen.getByText(/reached your free tier limit/i)).toBeInTheDocument();
  });
});
