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

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

import Portfolio from './Index';

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    usePage: vi.fn(() => ({
      props: {
        auth: {
          user: {
            name: 'Test User',
            email: 'test@example.com',
          },
        },
        features: {
          notifications: false,
        },
      },
    })),
    Head: ({ title }: { title: string }) => <title>{title}</title>,
    Link: ({ children, href }: { children: React.ReactNode; href: string }) => (
      <a href={href}>{children}</a>
    ),
  };
});

// Mock the useTheme hook to avoid needing ThemeProvider
vi.mock('@/Components/theme/use-theme', () => ({
  useTheme: vi.fn(() => ({
    theme: 'system',
    setTheme: vi.fn(),
    resolvedTheme: 'light',
  })),
}));

vi.mock('sonner', () => ({
  toast: {
    success: vi.fn(),
    error: vi.fn(),
  },
}));

// Mock child components
vi.mock('@/Components/Portfolio/PortfolioMetricsCards', () => ({
  default: ({
    totalClicks,
    totalImpressions,
    averageCtr,
  }: {
    totalClicks: number;
    totalImpressions: number;
    averageCtr: number;
  }) => (
    <div data-testid="portfolio-metrics-cards">
      <span>Total Clicks: {totalClicks}</span>
      <span>Total Impressions: {totalImpressions}</span>
      <span>Average CTR: {averageCtr}</span>
    </div>
  ),
}));

vi.mock('@/Components/Portfolio/SiteComparisonTable', () => ({
  default: ({ sites }: { sites: unknown[] }) => (
    <div data-testid="site-comparison-table">
      <span>Sites: {sites.length}</span>
    </div>
  ),
}));

vi.mock('@/Components/Portfolio/CrossSiteTrafficChart', () => ({
  default: () => <div data-testid="cross-site-traffic-chart">Chart</div>,
}));

const mockedUsePage = vi.mocked(usePage);

const mockSiteWithStats = (id: number, name: string) => ({
  id,
  name,
  domain: `${name.toLowerCase()}.com`,
  traffic_change_percent: 10.5,
  pending_recommendations: 3,
  last_analysis_date: '2026-02-20',
  attention_flags: {
    declining_traffic: false,
    stale_analysis: false,
    has_pending_recommendations: true,
  },
  latest_clicks: 100,
  latest_impressions: 1000,
  health_score: 75.5,
  previous_health_score: null,
  health_status: null,
  health_trend: null,
  recommendations_applied_count: 2,
  clicks_gained: 150,
  roi_trend: 'up' as const,
  active_alerts_count: 0,
});

const defaultAgencyStats = {
  total_recommendations_applied: 0,
  total_clicks_gained: 0,
  sites_with_positive_roi: 0,
  sites_with_active_alerts: 0,
};

const defaultProps = {
  aggregate_metrics: {
    total_clicks: 0,
    total_impressions: 0,
    average_ctr: 0,
  },
  agency_stats: defaultAgencyStats,
  sites_with_stats: [],
  time_series_data: [],
};

describe('Portfolio', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockedUsePage.mockReturnValue({
      props: {
        auth: {
          user: {
            name: 'Test User',
            email: 'test@example.com',
          },
        },
        features: {
          notifications: false,
        },
      },
    } as unknown as ReturnType<typeof usePage>);
  });

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

  describe('rendering', () => {
    it('renders the portfolio page', () => {
      render(<Portfolio {...defaultProps} />);

      // Portfolio Overview appears multiple times (title and heading)
      const portfolioTexts = screen.getAllByText('Portfolio Overview');
      expect(portfolioTexts.length).toBeGreaterThan(0);
    });

    it('renders the subtitle', () => {
      render(<Portfolio {...defaultProps} />);

      expect(screen.getByText('Aggregate insights across all your sites')).toBeInTheDocument();
    });
  });

  // ============================================
  // Empty state tests (0 sites)
  // ============================================

  describe('empty state', () => {
    it('renders empty state when no sites exist', () => {
      render(<Portfolio {...defaultProps} />);

      expect(screen.getByText('No Sites Found')).toBeInTheDocument();
      expect(
        screen.getByText('Add your first site to start building your portfolio'),
      ).toBeInTheDocument();
    });

    it('renders Add Your First Site button in empty state', () => {
      render(<Portfolio {...defaultProps} />);

      const addButton = screen.getByRole('link', { name: /add your first site/i });
      expect(addButton).toBeInTheDocument();
      expect(addButton).toHaveAttribute('href', '/sites/create');
    });

    it('does not render portfolio content in empty state', () => {
      render(<Portfolio {...defaultProps} />);

      expect(screen.queryByTestId('portfolio-metrics-cards')).not.toBeInTheDocument();
      expect(screen.queryByTestId('site-comparison-table')).not.toBeInTheDocument();
    });
  });

  // ============================================
  // Single site state tests
  // ============================================

  describe('single site state', () => {
    const singleSiteProps = {
      ...defaultProps,
      sites_with_stats: [mockSiteWithStats(1, 'Site1')],
    };

    it('renders alert when only one site exists', () => {
      render(<Portfolio {...singleSiteProps} />);

      expect(screen.getByText('Portfolio View Requires Multiple Sites')).toBeInTheDocument();
    });

    it('shows correct message for single site', () => {
      render(<Portfolio {...singleSiteProps} />);

      expect(screen.getByText(/you currently have 1 site/i)).toBeInTheDocument();
      expect(
        screen.getByText(/add at least one more site to unlock portfolio-level analytics/i),
      ).toBeInTheDocument();
    });

    it('renders Add Site button in single site alert', () => {
      render(<Portfolio {...singleSiteProps} />);

      const addButton = screen.getByRole('link', { name: /add site/i });
      expect(addButton).toBeInTheDocument();
      expect(addButton).toHaveAttribute('href', '/sites/create');
    });

    it('does not render portfolio content with single site', () => {
      render(<Portfolio {...singleSiteProps} />);

      expect(screen.queryByTestId('portfolio-metrics-cards')).not.toBeInTheDocument();
      expect(screen.queryByTestId('site-comparison-table')).not.toBeInTheDocument();
    });
  });

  // ============================================
  // Full portfolio state tests (2+ sites)
  // ============================================

  describe('full portfolio state', () => {
    const fullPortfolioProps = {
      aggregate_metrics: {
        total_clicks: 500,
        total_impressions: 5000,
        average_ctr: 10.0,
      },
      agency_stats: defaultAgencyStats,
      sites_with_stats: [mockSiteWithStats(1, 'Site1'), mockSiteWithStats(2, 'Site2')],
      time_series_data: [
        { date: '2026-02-01', clicks: 50, impressions: 500 },
        { date: '2026-02-02', clicks: 60, impressions: 600 },
      ],
    };

    it('does not render alert with two or more sites', () => {
      render(<Portfolio {...fullPortfolioProps} />);

      expect(screen.queryByText('Portfolio View Requires Multiple Sites')).not.toBeInTheDocument();
    });

    it('renders portfolio metrics cards', () => {
      render(<Portfolio {...fullPortfolioProps} />);

      expect(screen.getByTestId('portfolio-metrics-cards')).toBeInTheDocument();
      expect(screen.getByText('Total Clicks: 500')).toBeInTheDocument();
      expect(screen.getByText('Total Impressions: 5000')).toBeInTheDocument();
      expect(screen.getByText('Average CTR: 10')).toBeInTheDocument();
    });

    it('renders site comparison section', () => {
      render(<Portfolio {...fullPortfolioProps} />);

      expect(screen.getByText('Site Comparison')).toBeInTheDocument();
      expect(
        screen.getByText('Compare traffic changes, ROI, and pending actions across all your sites'),
      ).toBeInTheDocument();
    });

    it('renders site comparison table', () => {
      render(<Portfolio {...fullPortfolioProps} />);

      const table = screen.getByTestId('site-comparison-table');
      expect(table).toBeInTheDocument();
      expect(screen.getByText('Sites: 2')).toBeInTheDocument();
    });

    it('renders traffic trends section', () => {
      render(<Portfolio {...fullPortfolioProps} />);

      expect(screen.getByText('Traffic Trends')).toBeInTheDocument();
      expect(
        screen.getByText('Performance trends across all sites over the last 30 days'),
      ).toBeInTheDocument();
    });

    it('renders traffic chart when data is available', () => {
      render(<Portfolio {...fullPortfolioProps} />);

      expect(screen.getByTestId('cross-site-traffic-chart')).toBeInTheDocument();
    });

    it('renders empty state in chart when no time series data', () => {
      const propsWithoutTimeSeriesData = {
        ...fullPortfolioProps,
        time_series_data: [],
      };

      render(<Portfolio {...propsWithoutTimeSeriesData} />);

      expect(screen.getByText('No Traffic Data Available')).toBeInTheDocument();
      expect(
        screen.getByText('Sync your sites with Google Search Console to see traffic trends'),
      ).toBeInTheDocument();
    });
  });

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

  describe('layout integration', () => {
    it('uses DashboardLayout', () => {
      const { container } = render(<Portfolio {...defaultProps} />);

      // Dashboard should render within a layout container
      expect(container.querySelector('.min-h-screen')).toBeInTheDocument();
    });

    it('renders within container with proper spacing', () => {
      const { container } = render(<Portfolio {...defaultProps} />);

      expect(container.querySelector('.container')).toBeInTheDocument();
    });
  });

  // ============================================
  // Accessibility tests
  // ============================================

  describe('accessibility', () => {
    it('sets the page title', () => {
      render(<Portfolio {...defaultProps} />);

      // The Head component is mocked to render a title element
      expect(document.querySelector('title')).toHaveTextContent('Portfolio Overview');
    });

    it('has proper heading structure', () => {
      render(<Portfolio {...defaultProps} />);

      // Should have main heading
      expect(screen.getByRole('heading', { name: 'Portfolio Overview' })).toBeInTheDocument();
    });

    it('add site links are keyboard accessible', () => {
      render(<Portfolio {...defaultProps} />);

      const addButton = screen.getByRole('link', { name: /add your first site/i });
      expect(addButton).toHaveAttribute('href', '/sites/create');
    });
  });

  // ============================================
  // Edge cases
  // ============================================

  describe('edge cases', () => {
    it('handles zero metrics gracefully', () => {
      const zeroMetricsProps = {
        aggregate_metrics: {
          total_clicks: 0,
          total_impressions: 0,
          average_ctr: 0,
        },
        agency_stats: defaultAgencyStats,
        sites_with_stats: [mockSiteWithStats(1, 'Site1'), mockSiteWithStats(2, 'Site2')],
        time_series_data: [],
      };

      render(<Portfolio {...zeroMetricsProps} />);

      expect(screen.getByText('Total Clicks: 0')).toBeInTheDocument();
      expect(screen.getByText('Total Impressions: 0')).toBeInTheDocument();
      expect(screen.getByText('Average CTR: 0')).toBeInTheDocument();
    });

    it('handles plural correctly with exactly 2 sites', () => {
      const twoSitesProps = {
        ...defaultProps,
        sites_with_stats: [mockSiteWithStats(1, 'Site1'), mockSiteWithStats(2, 'Site2')],
      };

      render(<Portfolio {...twoSitesProps} />);

      // Should not show the "requires multiple sites" alert
      expect(screen.queryByText(/you currently have 1 site/i)).not.toBeInTheDocument();
    });

    it('handles many sites', () => {
      const manySitesProps = {
        ...defaultProps,
        sites_with_stats: [
          mockSiteWithStats(1, 'Site1'),
          mockSiteWithStats(2, 'Site2'),
          mockSiteWithStats(3, 'Site3'),
          mockSiteWithStats(4, 'Site4'),
          mockSiteWithStats(5, 'Site5'),
        ],
      };

      render(<Portfolio {...manySitesProps} />);

      expect(screen.getByText('Sites: 5')).toBeInTheDocument();
    });
  });

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

  describe('interactions', () => {
    const fullPortfolioProps = {
      aggregate_metrics: { total_clicks: 500, total_impressions: 5000, average_ctr: 10.0 },
      agency_stats: defaultAgencyStats,
      sites_with_stats: [mockSiteWithStats(1, 'Site1'), mockSiteWithStats(2, 'Site2')],
      time_series_data: [{ date: '2026-02-01', clicks: 50, impressions: 500 }],
    };

    it('Export CSV button is visible and clickable via userEvent', async () => {
      const { default: userEvent } = await import('@testing-library/user-event');
      const { router } = await import('@inertiajs/react');
      const postSpy = vi.spyOn(router, 'post').mockImplementation(() => {});
      const user = userEvent.setup();

      render(<Portfolio {...fullPortfolioProps} />);

      const exportBtn = screen.getByRole('button', { name: /export roi csv/i });
      await user.click(exportBtn);

      expect(exportBtn).toBeInTheDocument();
      postSpy.mockRestore();
    });

    it('Bulk Analyze button is visible and clickable via userEvent', async () => {
      const { default: userEvent } = await import('@testing-library/user-event');
      const { router } = await import('@inertiajs/react');
      const postSpy = vi.spyOn(router, 'post').mockImplementation(() => {});
      const user = userEvent.setup();

      render(<Portfolio {...fullPortfolioProps} />);

      const bulkBtn = screen.getByRole('button', { name: /run analysis on all sites/i });
      await user.click(bulkBtn);

      expect(bulkBtn).toBeInTheDocument();
      postSpy.mockRestore();
    });

    it('shows error toast and re-enables buttons when bulk analyze fails', async () => {
      const { default: userEvent } = await import('@testing-library/user-event');
      const { router } = await import('@inertiajs/react');
      const { toast } = await import('sonner');

      // Simulate a failed POST: invoke onError + onFinish synchronously, like Inertia's router does after a network/server error.
      const postSpy = vi.spyOn(router, 'post').mockImplementation(((
        _url: string,
        _data: unknown,
        options?: Record<string, unknown>,
      ) => {
        (options?.onError as ((errors: Record<string, string>) => void) | undefined)?.({
          error: 'fail',
        });
        (options?.onFinish as (() => void) | undefined)?.();
      }) as unknown as typeof router.post);

      const user = userEvent.setup();
      render(<Portfolio {...fullPortfolioProps} />);

      const analyzeBtn = screen.getByRole('button', { name: /run analysis on all sites/i });
      const reportBtn = screen.getByRole('button', { name: /generate reports for all sites/i });

      await user.click(analyzeBtn);

      expect(vi.mocked(toast.error)).toHaveBeenCalledWith(
        expect.stringMatching(/Couldn't queue bulk analysis/i),
      );
      // After onFinish, both buttons re-enable so the user can retry.
      expect(analyzeBtn).not.toBeDisabled();
      expect(reportBtn).not.toBeDisabled();

      postSpy.mockRestore();
    });

    it('disables both bulk-action buttons while one request is in flight', async () => {
      const { default: userEvent } = await import('@testing-library/user-event');
      const { router } = await import('@inertiajs/react');

      // Capture the post call but never invoke the callbacks — request stays "in flight".
      const postSpy = vi
        .spyOn(router, 'post')
        .mockImplementation((() => {}) as unknown as typeof router.post);

      const user = userEvent.setup();
      render(<Portfolio {...fullPortfolioProps} />);

      const analyzeBtn = screen.getByRole('button', { name: /run analysis on all sites/i });
      const reportBtn = screen.getByRole('button', { name: /generate reports for all sites/i });

      await user.click(analyzeBtn);

      expect(analyzeBtn).toBeDisabled();
      expect(reportBtn).toBeDisabled();

      postSpy.mockRestore();
    });

    it('shows error toast and re-enables buttons when bulk report fails', async () => {
      const { default: userEvent } = await import('@testing-library/user-event');
      const { router } = await import('@inertiajs/react');
      const { toast } = await import('sonner');

      const postSpy = vi.spyOn(router, 'post').mockImplementation(((
        _url: string,
        _data: unknown,
        options?: Record<string, unknown>,
      ) => {
        (options?.onError as ((errors: Record<string, string>) => void) | undefined)?.({
          error: 'fail',
        });
        (options?.onFinish as (() => void) | undefined)?.();
      }) as unknown as typeof router.post);

      const user = userEvent.setup();
      render(<Portfolio {...fullPortfolioProps} />);

      const reportBtn = screen.getByRole('button', { name: /generate reports for all sites/i });
      await user.click(reportBtn);

      expect(vi.mocked(toast.error)).toHaveBeenCalledWith(
        expect.stringMatching(/Couldn't queue bulk reports/i),
      );
      expect(reportBtn).not.toBeDisabled();

      postSpy.mockRestore();
    });
  });
});
