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

import RecommendationsIndex from './Index';

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    Head: ({ title }: { title: string }) => <title>{title}</title>,
    Link: ({ children, href }: { children: React.ReactNode; href: string }) => (
      <a href={href}>{children}</a>
    ),
    router: { reload: vi.fn() },
    usePage: () => ({
      props: {
        ai_max_batch_size: 50,
        polling_interval_ms: 3000,
        ai_status: { mode: 'byok', bundled_remaining: 0, bundled_limit: 0 },
        ai_consent: { has_bundled_consent: true },
      },
    }),
  };
});

vi.mock('@/Layouts/DashboardLayout', () => ({
  default: ({ children }: { children: React.ReactNode }) => (
    <div data-testid="dashboard-layout">{children}</div>
  ),
}));

vi.mock('@/Components/Navigation/SiteNav', () => ({
  default: () => <div data-testid="site-nav" />,
}));

vi.mock('@/Components/Recommendations/ActionTypeFilter', () => ({
  default: () => <div data-testid="action-type-filter" />,
}));

vi.mock('@/Components/Recommendations/RecommendationCard', () => ({
  default: ({ recommendation }: { recommendation: { title: string } }) => (
    <div data-testid="recommendation-card">{recommendation.title}</div>
  ),
}));

vi.mock('@/Components/Recommendations/RecommendationCardSkeleton', () => ({
  default: () => <div data-testid="recommendation-card-skeleton" />,
}));

vi.mock('@/Components/Recommendations/BatchGenerateModal', () => ({
  default: () => <div data-testid="batch-generate-modal" />,
}));

vi.mock('@/Components/Shared/InertiaPagination', () => ({
  default: () => <div data-testid="pagination" />,
}));

vi.mock('@/Components/Ai/AiJobProgress', () => ({
  default: () => <div data-testid="ai-job-progress" />,
}));

vi.mock('@/Components/ui/empty-state', () => ({
  EmptyState: ({
    title,
    description,
    action,
  }: {
    title: string;
    description: string;
    action?: React.ReactNode;
  }) => (
    <div data-testid="empty-state">
      {title}: {description}
      {action}
    </div>
  ),
}));

vi.mock('@/Components/ui/button', () => ({
  Button: ({
    children,
    onClick,
    asChild,
  }: {
    children: React.ReactNode;
    onClick?: () => void;
    asChild?: boolean;
  }) => (asChild ? <>{children}</> : <button onClick={onClick}>{children}</button>),
}));

vi.mock('@/Components/ui/info-tooltip', () => ({
  InfoTooltip: ({ content, side }: { content: string; side?: string }) => (
    <span data-testid="info-tooltip" data-content={content} data-side={side}>
      ⓘ
    </span>
  ),
}));

const emptyProps = {
  site: { id: 1, name: 'Test Site', domain: 'https://test.com' },
  analysis_run: null,
  recommendations: { data: [], links: [], current_page: 1, last_page: 1 },
  filters: { action_type: null, lifecycle_status: null, sort: null },
  counts: { total: 0, noindex: 0, content_rewrite: 0 },
  ai_available: true,
  ai_job: null,
  batch_job: null,
  ai_settings_summary: {
    model: 'gpt-4o-mini',
    temperature: 0.7,
    top_p: 1,
    max_output_tokens: 4096,
  },
  gsc_connected: true,
};

const withRecsProps = {
  ...emptyProps,
  analysis_run: { id: 1, status: 'completed', completed_at: '2026-01-15' },
  recommendations: {
    data: [
      {
        id: 1,
        page_url: '/page-1',
        action_type: 'content_rewrite',
        impact_score: 85,
        confidence_score: 0.9,
        title: 'Rewrite page',
        reasoning: 'Traffic dropped',
        evidence: { clicks_before: 100, clicks_after: 50, delta_percent: -50 },
        has_draft: false,
        latest_draft_id: null,
        lifecycle_status: 'pending',
      },
    ],
    links: [],
    current_page: 1,
    last_page: 1,
  },
  counts: { total: 1, noindex: 0, content_rewrite: 1 },
};

describe('Recommendations/Index', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.stubGlobal(
      'route',
      vi.fn((...args: string[]) => `/mock-route/${args[0]}`),
    );
  });

  // ============================================
  // Rendering tests
  // ============================================

  describe('rendering', () => {
    it('renders page title with site name', () => {
      render(<RecommendationsIndex {...emptyProps} />);

      expect(document.querySelector('title')).toHaveTextContent('Test Site - Recommendations');
    });

    it('renders heading "Recommendations"', () => {
      render(<RecommendationsIndex {...emptyProps} />);

      expect(screen.getByRole('heading', { name: 'Recommendations' })).toBeInTheDocument();
    });
  });

  // ============================================
  // Empty state tests
  // ============================================

  describe('empty states', () => {
    it('shows empty state when no analysis run', () => {
      render(<RecommendationsIndex {...emptyProps} />);

      // When gscConnected and no analysis run, shows ActivationPromptCard (run_analysis variant)
      const link = screen.getByRole('link', { name: /run your first analysis/i });
      expect(link).toBeInTheDocument();
    });

    it('shows empty state when analysis done but no recommendations', () => {
      render(
        <RecommendationsIndex
          {...emptyProps}
          analysis_run={{ id: 1, status: 'completed', completed_at: '2026-01-15' }}
        />,
      );

      const emptyState = screen.getByTestId('empty-state');
      expect(emptyState).toHaveTextContent('No recommendations yet');
      expect(emptyState).toHaveTextContent('looks healthy');
    });
  });

  // ============================================
  // Recommendations list tests
  // ============================================

  describe('recommendations list', () => {
    it('shows recommendations list when data exists', () => {
      render(<RecommendationsIndex {...withRecsProps} />);

      expect(screen.getByTestId('action-type-filter')).toBeInTheDocument();
      expect(screen.getByTestId('recommendation-card')).toBeInTheDocument();
      expect(screen.getByText('Rewrite page')).toBeInTheDocument();
      expect(screen.getByTestId('pagination')).toBeInTheDocument();
    });
  });

  // ============================================
  // Generate All Drafts button tests
  // ============================================

  describe('Generate All Drafts button', () => {
    it('shows button when user has AI key and content_rewrite recommendations', () => {
      render(<RecommendationsIndex {...withRecsProps} />);

      expect(screen.getByRole('button', { name: /generate all drafts/i })).toBeInTheDocument();
    });

    it('hides button when user has no AI key', () => {
      render(<RecommendationsIndex {...withRecsProps} ai_available={false} />);

      expect(
        screen.queryByRole('button', { name: /generate all drafts/i }),
      ).not.toBeInTheDocument();
    });

    it('hides button when there are no content_rewrite recommendations', () => {
      render(
        <RecommendationsIndex
          {...withRecsProps}
          counts={{ total: 1, noindex: 1, content_rewrite: 0 }}
        />,
      );

      expect(
        screen.queryByRole('button', { name: /generate all drafts/i }),
      ).not.toBeInTheDocument();
    });
  });

  // ============================================
  // AI settings summary tests
  // ============================================

  describe('AI settings summary', () => {
    function renderAndExpandAiSettings() {
      render(<RecommendationsIndex {...withRecsProps} />);
      // AI settings are inside a Collapsible (collapsed by default) — expand it
      const trigger = screen.getByRole('button', { name: /AI Settings:/i });
      fireEvent.click(trigger);
    }

    it('shows AI settings summary when recommendations exist', () => {
      renderAndExpandAiSettings();

      // Model name appears in trigger + expanded content
      const modelElements = screen.getAllByText('gpt-4o-mini', { exact: false });
      expect(modelElements.length).toBeGreaterThan(0);
      expect(screen.getByText(/Temp:/)).toBeInTheDocument();
      expect(screen.getByText(/Top P:/)).toBeInTheDocument();
      expect(screen.getByText(/Max tokens:/)).toBeInTheDocument();
    });

    it('renders InfoTooltip for model setting', () => {
      renderAndExpandAiSettings();

      const tooltips = screen.getAllByTestId('info-tooltip');
      const modelTooltip = tooltips.find((tooltip) =>
        tooltip.getAttribute('data-content')?.includes('AI model used for content generation'),
      );

      expect(modelTooltip).toBeInTheDocument();
      expect(modelTooltip?.getAttribute('data-content')).toContain(
        'More capable models may produce better results but cost more per token',
      );
      expect(modelTooltip?.getAttribute('data-side')).toBe('top');
    });

    it('renders InfoTooltip for temperature setting', () => {
      renderAndExpandAiSettings();

      const tooltips = screen.getAllByTestId('info-tooltip');
      const tempTooltip = tooltips.find((tooltip) =>
        tooltip.getAttribute('data-content')?.includes('Controls randomness'),
      );

      expect(tempTooltip).toBeInTheDocument();
      expect(tempTooltip?.getAttribute('data-content')).toContain(
        'Lower values (0.0-0.3) produce focused, deterministic output',
      );
      expect(tempTooltip?.getAttribute('data-content')).toContain(
        'Higher values (0.7-2.0) produce more creative, varied output',
      );
      expect(tempTooltip?.getAttribute('data-side')).toBe('top');
    });

    it('renders InfoTooltip for top_p setting', () => {
      renderAndExpandAiSettings();

      const tooltips = screen.getAllByTestId('info-tooltip');
      const topPTooltip = tooltips.find((tooltip) =>
        tooltip.getAttribute('data-content')?.includes('Controls diversity via nucleus sampling'),
      );

      expect(topPTooltip).toBeInTheDocument();
      expect(topPTooltip?.getAttribute('data-content')).toContain('1.0 considers all tokens');
      expect(topPTooltip?.getAttribute('data-content')).toContain(
        'Lower values (e.g. 0.1) only consider the most likely tokens',
      );
      expect(topPTooltip?.getAttribute('data-side')).toBe('top');
    });

    it('renders InfoTooltip for max_output_tokens setting', () => {
      renderAndExpandAiSettings();

      const tooltips = screen.getAllByTestId('info-tooltip');
      const maxTokensTooltip = tooltips.find((tooltip) =>
        tooltip.getAttribute('data-content')?.includes('Maximum length of the generated content'),
      );

      expect(maxTokensTooltip).toBeInTheDocument();
      expect(maxTokensTooltip?.getAttribute('data-content')).toContain(
        'One token is roughly 4 characters of English text',
      );
      expect(maxTokensTooltip?.getAttribute('data-side')).toBe('top');
    });

    it('renders exactly 5 InfoTooltips in AI settings and lifecycle filter', () => {
      renderAndExpandAiSettings();

      const tooltips = screen.getAllByTestId('info-tooltip');
      expect(tooltips).toHaveLength(5);
    });

    it('does not render AI settings summary when no recommendations', () => {
      render(<RecommendationsIndex {...emptyProps} />);

      expect(screen.queryByTestId('info-tooltip')).not.toBeInTheDocument();
    });
  });

  // ============================================
  // AI job progress tests
  // ============================================

  describe('AI job progress', () => {
    it('shows AiJobProgress when AI job is active', () => {
      render(
        <RecommendationsIndex
          {...withRecsProps}
          ai_job={{
            id: 1,
            status: 'processing',
            total_recommendations: 5,
            completed_recommendations: 2,
            progress_percent: 40,
          }}
        />,
      );

      expect(screen.getByTestId('ai-job-progress')).toBeInTheDocument();
    });

    it('does not show AiJobProgress when no AI job', () => {
      render(<RecommendationsIndex {...withRecsProps} />);

      expect(screen.queryByTestId('ai-job-progress')).not.toBeInTheDocument();
    });
  });

  // ============================================
  // Skeleton state tests
  // ============================================

  describe('skeleton states', () => {
    it('shows skeleton cards matching total_recommendations when AI job is processing', () => {
      render(
        <RecommendationsIndex
          {...withRecsProps}
          ai_job={{
            id: 1,
            status: 'processing',
            total_recommendations: 5,
            completed_recommendations: 2,
            progress_percent: 40,
          }}
        />,
      );

      const skeletons = screen.getAllByTestId('recommendation-card-skeleton');
      expect(skeletons).toHaveLength(5);
      expect(screen.queryByTestId('recommendation-card')).not.toBeInTheDocument();
    });

    it('shows skeleton cards matching total_recommendations when AI job is pending', () => {
      render(
        <RecommendationsIndex
          {...withRecsProps}
          ai_job={{
            id: 1,
            status: 'pending',
            total_recommendations: 3,
            completed_recommendations: 0,
            progress_percent: 0,
          }}
        />,
      );

      const skeletons = screen.getAllByTestId('recommendation-card-skeleton');
      expect(skeletons).toHaveLength(3);
      expect(screen.queryByTestId('recommendation-card')).not.toBeInTheDocument();
    });

    it('caps skeleton count at 10 for large batches', () => {
      render(
        <RecommendationsIndex
          {...withRecsProps}
          ai_job={{
            id: 1,
            status: 'processing',
            total_recommendations: 25,
            completed_recommendations: 0,
            progress_percent: 0,
          }}
        />,
      );

      const skeletons = screen.getAllByTestId('recommendation-card-skeleton');
      expect(skeletons).toHaveLength(10);
    });

    it('shows recommendation cards when AI job is completed', () => {
      render(
        <RecommendationsIndex
          {...withRecsProps}
          ai_job={{
            id: 1,
            status: 'completed',
            total_recommendations: 5,
            completed_recommendations: 5,
            progress_percent: 100,
          }}
        />,
      );

      expect(screen.getByTestId('recommendation-card')).toBeInTheDocument();
      expect(screen.queryByTestId('recommendation-card-skeleton')).not.toBeInTheDocument();
    });

    it('shows recommendation cards when no AI job', () => {
      render(<RecommendationsIndex {...withRecsProps} ai_job={null} />);

      expect(screen.getByTestId('recommendation-card')).toBeInTheDocument();
      expect(screen.queryByTestId('recommendation-card-skeleton')).not.toBeInTheDocument();
    });
  });

  // ============================================
  // Layout tests
  // ============================================

  describe('layout', () => {
    it('assigns DashboardLayout as layout', () => {
      expect(RecommendationsIndex.layout).toBeDefined();
    });
  });

  // ============================================
  // userEvent interaction tests
  // ============================================

  describe('interactions', () => {
    it('expands AI settings collapsible via userEvent click', async () => {
      const { default: userEvent } = await import('@testing-library/user-event');
      const user = userEvent.setup();

      render(<RecommendationsIndex {...withRecsProps} />);

      const trigger = screen.getByRole('button', { name: /AI Settings:/i });
      await user.click(trigger);

      expect(screen.getAllByTestId('info-tooltip').length).toBeGreaterThan(0);
    });

    it('Generate All Drafts button is clickable via userEvent', async () => {
      const { default: userEvent } = await import('@testing-library/user-event');
      const user = userEvent.setup();

      render(<RecommendationsIndex {...withRecsProps} />);

      const btn = screen.getByRole('button', { name: /generate all drafts/i });
      await user.click(btn);

      // Button renders and is interactive (opens modal or triggers navigation)
      expect(btn).toBeInTheDocument();
    });
  });
});
