/**
 * Tests for PlanEditorWizard Component Validation
 *
 * TDD: These tests define the expected validation behavior.
 */

import React from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { PlanEditorWizard } from '../PlanEditorWizard';

// Create a fresh query client for each test
const createTestQueryClient = () =>
  new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  });

// Wrapper with QueryClientProvider
const createWrapper = () => {
  const queryClient = createTestQueryClient();
  return ({ children }: { children: React.ReactNode }) => (
    <QueryClientProvider client={queryClient}>{children}</QueryClientProvider>
  );
};

// Mock the hooks
vi.mock('../../../hooks/useBillingAdmin', () => ({
  useFeatures: () => ({
    data: [
      { id: 1, code: 'sms_enabled', name: 'SMS', feature_type: 'boolean' },
      { id: 2, code: 'max_users', name: 'Max Users', feature_type: 'integer' },
    ],
    isLoading: false,
  }),
  useAddOnProducts: () => ({
    data: [{ id: 1, code: 'addon1', name: 'Add-on 1', is_active: true }],
    isLoading: false,
  }),
  useCreatePlan: () => ({
    mutateAsync: vi.fn().mockResolvedValue({ id: 1, code: 'test' }),
    isPending: false,
  }),
  useCreatePlanVersion: () => ({
    mutateAsync: vi.fn().mockResolvedValue({ id: 1, version: 1 }),
    isPending: false,
  }),
  useUpdatePlan: () => ({
    mutateAsync: vi.fn().mockResolvedValue({ id: 1 }),
    isPending: false,
  }),
  useUpdatePlanVersion: () => ({
    mutateAsync: vi.fn().mockResolvedValue({ id: 1 }),
    isPending: false,
  }),
  useForceUpdatePlanVersion: () => ({
    mutateAsync: vi.fn().mockResolvedValue({ version: { id: 1 }, affected_count: 5 }),
    isPending: false,
  }),
  isForceUpdateConfirmRequired: (response: unknown) =>
    response !== null &&
    typeof response === 'object' &&
    'requires_confirm' in response &&
    (response as { requires_confirm: boolean }).requires_confirm === true,
}));

// Mock useCurrentUser from useAuth
vi.mock('../../../hooks/useAuth', () => ({
  useCurrentUser: () => ({
    data: { id: 1, role: 'superuser', email: 'admin@test.com' },
    isLoading: false,
  }),
}));

describe('PlanEditorWizard', () => {
  const defaultProps = {
    isOpen: true,
    onClose: vi.fn(),
    mode: 'create' as const,
  };

  beforeEach(() => {
    vi.clearAllMocks();
  });

  describe('Basics Step Validation', () => {
    it('requires plan name to proceed', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Plan code is entered but name is empty
      const codeInput = screen.getByLabelText(/plan code/i);
      await user.type(codeInput, 'test_plan');

      // Try to click Next
      const nextButton = screen.getByRole('button', { name: /next/i });
      expect(nextButton).toBeDisabled();
    });

    it('requires plan code to proceed', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Name is entered but code is empty
      const nameInput = screen.getByLabelText(/display name/i);
      await user.type(nameInput, 'Test Plan');

      // Next button should be disabled
      const nextButton = screen.getByRole('button', { name: /next/i });
      expect(nextButton).toBeDisabled();
    });

    it('allows proceeding when code and name are provided', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Enter both code and name
      const codeInput = screen.getByLabelText(/plan code/i);
      const nameInput = screen.getByLabelText(/display name/i);

      await user.type(codeInput, 'test_plan');
      await user.type(nameInput, 'Test Plan');

      // Next button should be enabled
      const nextButton = screen.getByRole('button', { name: /next/i });
      expect(nextButton).not.toBeDisabled();
    });

    it('sanitizes plan code to lowercase with no spaces', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      const codeInput = screen.getByLabelText(/plan code/i);
      await user.type(codeInput, 'My Test Plan');

      // Should be sanitized
      expect(codeInput).toHaveValue('mytestplan');
    });
  });

  describe('Pricing Step Validation', () => {
    const goToPricingStep = async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Fill basics
      await user.type(screen.getByLabelText(/plan code/i), 'test');
      await user.type(screen.getByLabelText(/display name/i), 'Test');

      // Go to pricing step
      await user.click(screen.getByRole('button', { name: /next/i }));

      return user;
    };

    it('shows pricing step inputs', async () => {
      await goToPricingStep();

      expect(screen.getByLabelText(/monthly price/i)).toBeInTheDocument();
      expect(screen.getByLabelText(/yearly price/i)).toBeInTheDocument();
    });

    it('does not allow negative monthly price', async () => {
      const user = await goToPricingStep();

      const monthlyInput = screen.getByLabelText(/monthly price/i);
      await user.clear(monthlyInput);
      await user.type(monthlyInput, '-50');

      // Should show validation error or prevent input
      // The input type="number" with min="0" should prevent negative values
      expect(monthlyInput).toHaveAttribute('min', '0');
    });

    it('does not allow negative yearly price', async () => {
      const user = await goToPricingStep();

      const yearlyInput = screen.getByLabelText(/yearly price/i);
      await user.clear(yearlyInput);
      await user.type(yearlyInput, '-100');

      // Should have min attribute set
      expect(yearlyInput).toHaveAttribute('min', '0');
    });

    it('displays derived monthly equivalent for yearly price', async () => {
      const user = await goToPricingStep();

      const yearlyInput = screen.getByLabelText(/yearly price/i);
      await user.clear(yearlyInput);
      await user.type(yearlyInput, '120');

      // Should show the monthly equivalent ($10/mo)
      expect(screen.getByText(/\$10.*mo/i)).toBeInTheDocument();
    });
  });

  describe('Transaction Fees Validation', () => {
    const goToPricingStep = async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Fill basics and navigate
      await user.type(screen.getByLabelText(/plan code/i), 'test');
      await user.type(screen.getByLabelText(/display name/i), 'Test');
      await user.click(screen.getByRole('button', { name: /next/i }));

      return user;
    };

    it('validates fee percent is between 0 and 100', async () => {
      const user = await goToPricingStep();

      const feePercentInput = screen.getByLabelText(/fee percentage/i);

      // Should have min and max attributes
      expect(feePercentInput).toHaveAttribute('min', '0');
      expect(feePercentInput).toHaveAttribute('max', '100');
    });

    it('does not allow fee percent over 100', async () => {
      const user = await goToPricingStep();

      const feePercentInput = screen.getByLabelText(/fee percentage/i);
      await user.clear(feePercentInput);
      await user.type(feePercentInput, '150');

      // Should show validation warning
      expect(screen.getByText(/must be between 0 and 100/i)).toBeInTheDocument();
    });

    it('does not allow negative fee percent', async () => {
      const user = await goToPricingStep();

      const feePercentInput = screen.getByLabelText(/fee percentage/i);

      // Input has min="0" attribute to prevent negative values
      expect(feePercentInput).toHaveAttribute('min', '0');
    });

    it('shows transaction fee example calculation', async () => {
      const user = await goToPricingStep();

      // Should show example like "On a $100 transaction: $4.40 fee"
      expect(screen.getByText(/on a.*transaction/i)).toBeInTheDocument();
    });
  });

  describe('Wizard Navigation', () => {
    it('shows all wizard steps', () => {
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Should show step indicators (they have aria-label)
      expect(screen.getByRole('button', { name: /basics/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /pricing/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /features/i })).toBeInTheDocument();
      expect(screen.getByRole('button', { name: /display/i })).toBeInTheDocument();
    });

    it('navigates back from pricing to basics', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Fill basics and go to pricing
      await user.type(screen.getByLabelText(/plan code/i), 'test');
      await user.type(screen.getByLabelText(/display name/i), 'Test');
      await user.click(screen.getByRole('button', { name: /next/i }));

      // Should be on pricing step
      expect(screen.getByLabelText(/monthly price/i)).toBeInTheDocument();

      // Click back
      await user.click(screen.getByRole('button', { name: /back/i }));

      // Should be back on basics
      expect(screen.getByLabelText(/plan code/i)).toBeInTheDocument();
    });

    it('allows clicking step indicators to navigate', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Fill basics
      await user.type(screen.getByLabelText(/plan code/i), 'test');
      await user.type(screen.getByLabelText(/display name/i), 'Test');

      // Click on Pricing step indicator
      const pricingStep = screen.getByRole('button', { name: /pricing/i });
      await user.click(pricingStep);

      // Should navigate to pricing
      expect(screen.getByLabelText(/monthly price/i)).toBeInTheDocument();
    });
  });

  describe('Live Summary Panel', () => {
    it('shows plan name in summary', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      await user.type(screen.getByLabelText(/display name/i), 'My Amazing Plan');

      // Summary should show the plan name
      expect(screen.getByText('My Amazing Plan')).toBeInTheDocument();
    });

    it('shows price in summary after entering pricing', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Fill basics
      await user.type(screen.getByLabelText(/plan code/i), 'test');
      await user.type(screen.getByLabelText(/display name/i), 'Test');
      await user.click(screen.getByRole('button', { name: /next/i }));

      // Enter price
      const monthlyInput = screen.getByLabelText(/monthly price/i);
      await user.clear(monthlyInput);
      await user.type(monthlyInput, '29');

      // Summary should show the price
      expect(screen.getByText(/\$29/)).toBeInTheDocument();
    });

    it('shows selected features count in summary', async () => {
      const user = userEvent.setup();
      render(<PlanEditorWizard {...defaultProps} />, { wrapper: createWrapper() });

      // Navigate to features step
      await user.type(screen.getByLabelText(/plan code/i), 'test');
      await user.type(screen.getByLabelText(/display name/i), 'Test');
      await user.click(screen.getByRole('button', { name: /next/i })); // to pricing
      await user.click(screen.getByRole('button', { name: /next/i })); // to features

      // Select a feature
      const smsCheckbox = screen.getByRole('checkbox', { name: /sms/i });
      await user.click(smsCheckbox);

      // Summary should show feature count
      expect(screen.getByText(/1 feature/i)).toBeInTheDocument();
    });
  });

  describe('Create Version Confirmation', () => {
    it('shows grandfathering warning when editing version with subscribers', async () => {
      render(
        <PlanEditorWizard
          isOpen={true}
          onClose={vi.fn()}
          mode="edit"
          initialData={{
            id: 1,
            code: 'pro',
            name: 'Pro',
            version: {
              id: 1,
              subscriber_count: 5,
              name: 'Pro v1',
            },
          }}
        />,
        { wrapper: createWrapper() }
      );

      // Should show warning about subscribers and grandfathering
      expect(screen.getByText(/5/)).toBeInTheDocument();
      expect(screen.getByText(/subscriber/i)).toBeInTheDocument();
      expect(screen.getByText(/grandfathering/i)).toBeInTheDocument();
    });

    it('shows "Create New Version" confirmation for version with subscribers', async () => {
      const user = userEvent.setup();
      render(
        <PlanEditorWizard
          {...defaultProps}
          mode="edit"
          initialData={{
            id: 1,
            code: 'pro',
            name: 'Pro',
            version: {
              id: 1,
              subscriber_count: 5,
            },
          }}
        />,
        { wrapper: createWrapper() }
      );

      // Navigate to last step and try to save
      // The save button should mention "Create New Version"
      const saveButton = screen.queryByRole('button', { name: /create new version/i });
      expect(saveButton || screen.getByText(/new version/i)).toBeInTheDocument();
    });
  });

  describe('Form Submission', () => {
    it('calls onClose after successful creation', async () => {
      const onClose = vi.fn();
      const user = userEvent.setup();

      render(
        <PlanEditorWizard {...defaultProps} onClose={onClose} />,
        { wrapper: createWrapper() }
      );

      // Fill all required fields
      await user.type(screen.getByLabelText(/plan code/i), 'test');
      await user.type(screen.getByLabelText(/display name/i), 'Test');

      // Navigate through wizard
      await user.click(screen.getByRole('button', { name: /next/i })); // pricing
      await user.click(screen.getByRole('button', { name: /next/i })); // features
      await user.click(screen.getByRole('button', { name: /next/i })); // display

      // Submit
      const createButton = screen.getByRole('button', { name: /create plan/i });
      await user.click(createButton);

      await waitFor(() => {
        expect(onClose).toHaveBeenCalled();
      });
    });
  });

  describe('Force Update (Superuser)', () => {
    it('shows "Update Without Versioning" button for superuser editing plan with subscribers', async () => {
      const user = userEvent.setup();
      render(
        <PlanEditorWizard
          {...defaultProps}
          mode="edit"
          initialData={{
            id: 1,
            code: 'pro',
            name: 'Pro',
            version: {
              id: 1,
              subscriber_count: 5,
              name: 'Pro v1',
            },
          }}
        />,
        { wrapper: createWrapper() }
      );

      // Navigate to last step
      await user.click(screen.getByRole('button', { name: /pricing/i }));
      await user.click(screen.getByRole('button', { name: /features/i }));
      await user.click(screen.getByRole('button', { name: /display/i }));

      // Should show "Update Without Versioning" button
      expect(screen.getByRole('button', { name: /update without versioning/i })).toBeInTheDocument();
    });

    it('shows confirmation dialog when clicking "Update Without Versioning"', async () => {
      const user = userEvent.setup();
      render(
        <PlanEditorWizard
          {...defaultProps}
          mode="edit"
          initialData={{
            id: 1,
            code: 'pro',
            name: 'Pro',
            version: {
              id: 1,
              subscriber_count: 5,
              name: 'Pro v1',
            },
          }}
        />,
        { wrapper: createWrapper() }
      );

      // Navigate to last step
      await user.click(screen.getByRole('button', { name: /pricing/i }));
      await user.click(screen.getByRole('button', { name: /features/i }));
      await user.click(screen.getByRole('button', { name: /display/i }));

      // Click the force update button
      await user.click(screen.getByRole('button', { name: /update without versioning/i }));

      // Should show confirmation dialog with warning
      expect(screen.getByText(/warning: this will affect existing customers/i)).toBeInTheDocument();
      expect(screen.getByText(/5/)).toBeInTheDocument(); // subscriber count
      expect(screen.getByRole('button', { name: /yes, update all subscribers/i })).toBeInTheDocument();
    });

    it('can cancel force update confirmation', async () => {
      const user = userEvent.setup();
      render(
        <PlanEditorWizard
          {...defaultProps}
          mode="edit"
          initialData={{
            id: 1,
            code: 'pro',
            name: 'Pro',
            version: {
              id: 1,
              subscriber_count: 5,
              name: 'Pro v1',
            },
          }}
        />,
        { wrapper: createWrapper() }
      );

      // Navigate to last step
      await user.click(screen.getByRole('button', { name: /pricing/i }));
      await user.click(screen.getByRole('button', { name: /features/i }));
      await user.click(screen.getByRole('button', { name: /display/i }));

      // Click the force update button
      await user.click(screen.getByRole('button', { name: /update without versioning/i }));

      // Click Cancel
      const cancelButtons = screen.getAllByRole('button', { name: /cancel/i });
      await user.click(cancelButtons[0]); // First cancel is in the confirmation dialog

      // Confirmation dialog should be hidden, back to normal footer
      expect(screen.queryByText(/warning: this will affect existing customers/i)).not.toBeInTheDocument();
      expect(screen.getByRole('button', { name: /update without versioning/i })).toBeInTheDocument();
    });

    it('does not show "Update Without Versioning" for plans without subscribers', async () => {
      const user = userEvent.setup();
      render(
        <PlanEditorWizard
          {...defaultProps}
          mode="edit"
          initialData={{
            id: 1,
            code: 'pro',
            name: 'Pro',
            version: {
              id: 1,
              subscriber_count: 0, // No subscribers
              name: 'Pro v1',
            },
          }}
        />,
        { wrapper: createWrapper() }
      );

      // Navigate to last step
      await user.click(screen.getByRole('button', { name: /pricing/i }));
      await user.click(screen.getByRole('button', { name: /features/i }));
      await user.click(screen.getByRole('button', { name: /display/i }));

      // Should NOT show "Update Without Versioning" button
      expect(screen.queryByRole('button', { name: /update without versioning/i })).not.toBeInTheDocument();
    });
  });
});
