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

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

import { DASHBOARD_VIEWED } from '@/lib/event-catalog';

const { mockTrackProductEvent } = vi.hoisted(() => ({ mockTrackProductEvent: vi.fn() }));
vi.mock('@/lib/analytics', async (importOriginal) => {
  const actual = await importOriginal<typeof import('@/lib/analytics')>();
  return {
    ...actual,
    trackProductEvent: mockTrackProductEvent,
    trackEvent: vi.fn(),
    identifyUser: vi.fn(),
  };
});

import Dashboard from './Dashboard';

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',
  })),
}));

const mockedUsePage = vi.mocked(usePage);

const defaultProps = {
  funnel_metrics: {
    generated: 0,
    approved: 0,
    applied: 0,
  },
  dashboard_metrics: {
    total_clicks_28d: 0,
    pending_recommendations: 0,
    drafts_generated_month: 0,
    top_declining_pages: { data: [] },
    latest_analysis_at: null,
  },
  health_score: {
    score: null,
    previous_score: null,
    status: 'Unknown',
    trend: 'stable',
    calculated_at: null,
    components: {
      traffic_trend: null,
      content_quality: null,
      recommendation_completion: null,
      roi_performance: null,
      connection_health: null,
    },
  },
  content_intelligence: {
    cannibalization_count: 0,
    topic_cluster_count: 0,
    freshness_count: 0,
    keyword_opportunity_count: 0,
  },
} as unknown as Parameters<typeof Dashboard>[0];

describe('Dashboard', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockTrackProductEvent.mockClear();
    mockedUsePage.mockReturnValue({
      props: {
        auth: {
          user: {
            name: 'Test User',
            email: 'test@example.com',
          },
        },
        features: {
          notifications: false,
        },
      },
    } as unknown as ReturnType<typeof usePage>);
    vi.stubGlobal(
      'route',
      vi.fn(() => '/mock-route'),
    );
  });

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

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

      // Dashboard text appears multiple times (nav link, page title, etc.)
      const dashboardTexts = screen.getAllByText('Dashboard');
      expect(dashboardTexts.length).toBeGreaterThan(0);
    });

    it('renders the page heading', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByRole('heading', { name: /dashboard/i })).toBeInTheDocument();
    });

    it('always renders dashboard content even when not activated', () => {
      render(
        <Dashboard
          {...defaultProps}
          is_activated={true}
          wizard_completed={false}
          first_site={{ id: 1, name: 'Test', domain: 'test.com' }}
          sites={[
            {
              id: 1,
              name: 'Test',
              domain: 'test.com',
              has_gsc: false,
              gsc_synced: false,
              has_wp: false,
              wp_connected: false,
              has_analysis: false,
              has_reviewed_recommendation: false,
              has_ai_key: false,
              ai_available: false,
              current_role: 'owner',
            },
          ]}
        />,
      );

      // Should show dashboard heading (not be gated behind full-page wizard)
      expect(screen.getByRole('heading', { name: /dashboard/i })).toBeInTheDocument();
      expect(screen.getByText(/total clicks/i)).toBeInTheDocument();
    });

    it('shows setup progress card when not activated', () => {
      render(
        <Dashboard
          {...defaultProps}
          is_activated={false}
          wizard_completed={false}
          first_site={{ id: 1, name: 'Test', domain: 'test.com' }}
          sites={[
            {
              id: 1,
              name: 'Test',
              domain: 'test.com',
              has_gsc: false,
              gsc_synced: false,
              has_wp: false,
              wp_connected: false,
              has_analysis: false,
              has_reviewed_recommendation: false,
              has_ai_key: false,
              ai_available: false,
              current_role: 'owner',
            },
          ]}
        />,
      );

      expect(screen.getByText(/steps complete/i)).toBeInTheDocument();
    });
  });

  // ============================================
  // Analytics tracking
  // ============================================

  describe('analytics', () => {
    it('fires DASHBOARD_VIEWED on mount', () => {
      render(<Dashboard {...defaultProps} />);

      expect(mockTrackProductEvent).toHaveBeenCalledWith(
        DASHBOARD_VIEWED,
        expect.objectContaining({
          site_count: expect.any(Number),
        }),
      );
    });

    it('fires DASHBOARD_VIEWED exactly once per mount', () => {
      render(<Dashboard {...defaultProps} />);

      const dashboardViewedCalls = mockTrackProductEvent.mock.calls.filter(
        ([event]) => event === DASHBOARD_VIEWED,
      );
      expect(dashboardViewedCalls).toHaveLength(1);
    });

    it('passes site_id and site_count in DASHBOARD_VIEWED payload', () => {
      const site = {
        id: 42,
        name: 'My Site',
        domain: 'mysite.com',
        has_gsc: true,
        gsc_synced: true,
        has_wp: true,
        wp_connected: true,
        has_analysis: true,
        has_reviewed_recommendation: true,
        has_ai_key: true,
        ai_available: true,
        current_role: 'owner' as const,
      };
      render(
        <Dashboard
          {...defaultProps}
          first_site={{ id: 42, name: 'My Site', domain: 'mysite.com' }}
          sites={[site]}
        />,
      );

      expect(mockTrackProductEvent).toHaveBeenCalledWith(
        DASHBOARD_VIEWED,
        expect.objectContaining({ site_id: '42', site_count: 1 }),
      );
    });

    it('passes site_id as a string (not a number) to prevent PostHog type-mismatch', () => {
      const site = {
        id: 99,
        name: 'Type Check Site',
        domain: 'typecheck.com',
        has_gsc: false,
        gsc_synced: false,
        has_wp: false,
        wp_connected: false,
        has_analysis: false,
        has_reviewed_recommendation: false,
        has_ai_key: false,
        ai_available: false,
        current_role: 'owner' as const,
      };
      render(
        <Dashboard
          {...defaultProps}
          first_site={{ id: 99, name: 'Type Check Site', domain: 'typecheck.com' }}
          sites={[site]}
        />,
      );

      const call = mockTrackProductEvent.mock.calls.find(([event]) => event === DASHBOARD_VIEWED);
      expect(call).toBeDefined();
      // Independently verified: site_id must be the string '99', not the number 99.
      // PostHog and the warehouse treat site_id as a string dimension.
      expect(typeof call![1].site_id).toBe('string');
      expect(call![1].site_id).toBe('99');
    });
  });

  // ============================================
  // SEO metric stat cards
  // ============================================

  describe('SEO metric stat cards', () => {
    it('renders Total Clicks (28d) card', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText(/total clicks/i)).toBeInTheDocument();
    });

    it('renders Pending Recommendations card', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText('Pending Recommendations')).toBeInTheDocument();
    });

    it('renders Drafts Generated card', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText('Drafts Generated')).toBeInTheDocument();
    });

    it('renders Last Analyzed card', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText('Last Analyzed')).toBeInTheDocument();
    });

    it('displays metric values from dashboardMetrics', () => {
      const props = {
        ...defaultProps,
        dashboard_metrics: {
          total_clicks_28d: 12345,
          pending_recommendations: 7,
          drafts_generated_month: 3,
          top_declining_pages: { data: [] },
          latest_analysis_at: '2026-01-15T10:00:00Z',
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.getByText('12,345')).toBeInTheDocument();
      expect(screen.getByText('7')).toBeInTheDocument();
      expect(screen.getByText('3')).toBeInTheDocument();
    });

    it('shows "No analysis yet" when latest_analysis_at is null', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText('No analysis yet')).toBeInTheDocument();
    });
  });

  // ============================================
  // Top Declining Pages
  // ============================================

  describe('top declining pages', () => {
    it('renders Top Declining Pages section', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText('Top Declining Pages')).toBeInTheDocument();
    });

    it('shows empty state when no declining pages', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText(/No decline data yet/i)).toBeInTheDocument();
    });

    it('shows improved empty state description', () => {
      render(<Dashboard {...defaultProps} />);

      expect(
        screen.getByText(/highest-priority pages.*losing the most traffic/i),
      ).toBeInTheDocument();
    });

    it('renders declining pages when provided', () => {
      const props = {
        ...defaultProps,
        dashboard_metrics: {
          ...defaultProps.dashboard_metrics,
          top_declining_pages: {
            data: [
              {
                id: 1,
                page_url: 'https://example.com/page-1',
                delta_percent: -45.5,
                delta_absolute: -45,
              },
            ],
          },
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.getByText(/\/page-1/)).toBeInTheDocument();
    });
  });

  // ============================================
  // Site Health Score
  // ============================================

  describe('site health score', () => {
    it('renders Site Health Score section', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText('Site Health Score')).toBeInTheDocument();
    });

    it('shows empty state when health score is null', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText('No health score yet')).toBeInTheDocument();
      expect(screen.getByText(/Run your first analysis to calculate/i)).toBeInTheDocument();
    });

    it('renders health score when provided', () => {
      const props = {
        ...defaultProps,
        health_score: {
          ...defaultProps.health_score,
          score: 75,
          previous_score: null,
          calculated_at: '2026-01-15T10:00:00Z',
        },
      };
      render(<Dashboard {...props} />);

      // Should show the score value
      expect(screen.getByText('75')).toBeInTheDocument();
      expect(screen.getByText('/100')).toBeInTheDocument();
    });

    it('renders trend indicator when previous score exists', () => {
      const props = {
        ...defaultProps,
        health_score: {
          ...defaultProps.health_score,
          score: 80,
          previous_score: 70,
          calculated_at: '2026-01-15T10:00:00Z',
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.getByText('Since Last Analysis')).toBeInTheDocument();
      expect(screen.getByText('+10.0 points')).toBeInTheDocument();
    });

    it('shows declining trend when score decreased', () => {
      const props = {
        ...defaultProps,
        health_score: {
          ...defaultProps.health_score,
          score: 65,
          previous_score: 75,
          calculated_at: '2026-01-15T10:00:00Z',
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.getByText('-10.0 points')).toBeInTheDocument();
    });

    it('shows no change when delta is zero', () => {
      const props = {
        ...defaultProps,
        health_score: {
          ...defaultProps.health_score,
          score: 75,
          previous_score: 75,
          calculated_at: '2026-01-15T10:00:00Z',
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.getByText('No change')).toBeInTheDocument();
    });

    it('does not show trend indicator when no previous score', () => {
      const props = {
        ...defaultProps,
        health_score: {
          ...defaultProps.health_score,
          score: 75,
          previous_score: null,
          calculated_at: '2026-01-15T10:00:00Z',
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.queryByText('Since Last Analysis')).not.toBeInTheDocument();
    });

    it('displays calculation timestamp when provided', () => {
      const props = {
        ...defaultProps,
        health_score: {
          ...defaultProps.health_score,
          score: 75,
          previous_score: null,
          calculated_at: '2026-01-15T14:30:00Z',
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.getByText(/Last calculated/i)).toBeInTheDocument();
    });

    it('renders health score breakdown when score exists', () => {
      const props = {
        ...defaultProps,
        health_score: {
          ...defaultProps.health_score,
          score: 75,
          components: {
            traffic_trend: 80,
            content_quality: 70,
            recommendation_completion: 65,
            roi_performance: 85,
            connection_health: 90,
          },
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.getByText('Score Breakdown')).toBeInTheDocument();
      expect(screen.getByText('Traffic Trend')).toBeInTheDocument();
      expect(screen.getByText('Content Quality')).toBeInTheDocument();
      expect(screen.getByText(/30% weight/i)).toBeInTheDocument();
    });

    it('does not render breakdown when score is null', () => {
      const props = {
        ...defaultProps,
        health_score: {
          ...defaultProps.health_score,
          score: null,
        },
      };
      render(<Dashboard {...props} />);

      expect(screen.queryByText('Score Breakdown')).not.toBeInTheDocument();
    });
  });

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

  describe('layout integration', () => {
    it('uses DashboardLayout', () => {
      const { container } = render(<Dashboard {...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(<Dashboard {...defaultProps} />);

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

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

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

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

    it('renders stat card labels', () => {
      render(<Dashboard {...defaultProps} />);

      const labels = [
        'Total Clicks (28d)',
        'Pending Recommendations',
        'Drafts Generated',
        'Last Analyzed',
      ];
      labels.forEach((label) => {
        expect(screen.getByText(label)).toBeInTheDocument();
      });
    });

    it('renders contextual help icons for key metrics', () => {
      render(<Dashboard {...defaultProps} />);

      const helpButtons = screen.getAllByLabelText('More info');
      expect(helpButtons.length).toBeGreaterThanOrEqual(2);
    });
  });

  // ============================================
  // Analytics tracking
  // ============================================

  describe('analytics tracking', () => {
    it('fires dashboard_viewed event on mount', () => {
      render(<Dashboard {...defaultProps} />);

      expect(mockTrackProductEvent).toHaveBeenCalledWith('dashboard_viewed', expect.any(Object));
      expect(mockTrackProductEvent).toHaveBeenCalledTimes(1);
    });

    it('fires dashboard_viewed with site context', () => {
      mockedUsePage.mockReturnValue({
        props: {
          auth: { user: { name: 'Test User', email: 'test@example.com' } },
          features: { notifications: false },
          plan: 'pro',
        },
      } as unknown as ReturnType<typeof usePage>);

      render(
        <Dashboard
          {...defaultProps}
          first_site={{ id: 42, name: 'My Site', domain: 'mysite.com' }}
          sites={[
            {
              id: 42,
              name: 'My Site',
              domain: 'mysite.com',
              has_gsc: false,
              gsc_synced: false,
              has_wp: false,
              wp_connected: false,
              has_analysis: false,
              has_reviewed_recommendation: false,
              has_ai_key: false,
              ai_available: false,
              current_role: 'owner',
            },
          ]}
        />,
      );

      expect(mockTrackProductEvent).toHaveBeenCalledWith('dashboard_viewed', {
        site_id: '42',
        site_count: 1,
        plan_tier: 'pro',
      });
    });

    it('fires dashboard_viewed only once (mount only)', () => {
      const { rerender } = render(<Dashboard {...defaultProps} />);
      rerender(<Dashboard {...defaultProps} />);

      expect(mockTrackProductEvent).toHaveBeenCalledTimes(1);
    });
  });

  // ============================================
  // BannerStack — priority + collapse behavior (M4)
  // ============================================

  describe('banner stack', () => {
    const firstSite = { id: 1, name: 'Test', domain: 'test.com' };
    const sites = [
      {
        id: 1,
        name: 'Test',
        domain: 'test.com',
        has_gsc: true,
        gsc_synced: false,
        has_wp: true,
        wp_connected: true,
        has_analysis: false,
        has_reviewed_recommendation: false,
        has_ai_key: true,
        ai_available: true,
        current_role: 'owner' as const,
      },
    ];

    it('renders only the top 2 banners by priority when more than 2 are active', () => {
      // Active banners with these priorities:
      //   gsc-syncing (connection — highest)
      //   gsc-expiring (connection)
      //   step-2 (setup)
      //   streak-milestone (nudge — lowest)
      render(
        <Dashboard
          {...defaultProps}
          first_site={firstSite}
          sites={sites}
          gsc_syncing
          gsc_connected_no_run
          gsc_expiring_connections={[
            {
              site_id: 1,
              site_name: 'Test',
              expires_at: '2026-05-01',
              days_until_expiry: 3,
            },
          ]}
          streak_milestone_notification={{ id: 'milestone-1', weeks: 4 }}
        />,
      );

      // Top 2 visible: connection-priority banners.
      expect(screen.getByText('Your GSC data is syncing')).toBeInTheDocument();
      expect(screen.getByText(/Google Search Console Connection Expires Soon/)).toBeInTheDocument();

      // Lower-priority banners are collapsed behind the affordance.
      expect(screen.queryByText(/Step 2 of 3/)).not.toBeInTheDocument();
      expect(screen.queryByText(/4-Week SEO Streak/)).not.toBeInTheDocument();

      // Expander shows the hidden count.
      expect(screen.getByRole('button', { name: /2 more notifications/i })).toBeInTheDocument();
    });

    it('expands hidden banners when the "more notifications" button is clicked', async () => {
      const { default: userEvent } = await import('@testing-library/user-event');
      const user = userEvent.setup();

      render(
        <Dashboard
          {...defaultProps}
          first_site={firstSite}
          sites={sites}
          gsc_syncing
          gsc_connected_no_run
          gsc_expiring_connections={[
            {
              site_id: 1,
              site_name: 'Test',
              expires_at: '2026-05-01',
              days_until_expiry: 3,
            },
          ]}
          streak_milestone_notification={{ id: 'milestone-2', weeks: 4 }}
        />,
      );

      await user.click(screen.getByRole('button', { name: /2 more notifications/i }));

      expect(screen.getByText(/Step 2 of 3/)).toBeInTheDocument();
      expect(screen.getByText(/4-Week SEO Streak/)).toBeInTheDocument();
    });

    it('does not render any expander when 2 or fewer banners are active', () => {
      render(<Dashboard {...defaultProps} first_site={firstSite} sites={sites} gsc_syncing />);

      expect(screen.getByText('Your GSC data is syncing')).toBeInTheDocument();
      expect(screen.queryByRole('button', { name: /more notifications/i })).not.toBeInTheDocument();
    });

    it('still renders the streak milestone banner when no higher-priority banners exist', () => {
      // With no setup/connection banners, the nudge-priority milestone shows in the top 2.
      render(
        <Dashboard
          {...defaultProps}
          first_site={firstSite}
          sites={sites}
          is_activated
          has_reviewed_recommendation
          streak_milestone_notification={{ id: 'milestone-3', weeks: 8 }}
        />,
      );

      expect(screen.getByText(/8-Week SEO Streak/)).toBeInTheDocument();
    });
  });

  // ============================================
  // Recommendation Funnel tests
  // ============================================

  describe('recommendation funnel', () => {
    it('renders RecommendationFunnel component with metrics', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText('Recommendation Funnel')).toBeInTheDocument();
    });

    it('displays empty state when no recommendations', () => {
      render(<Dashboard {...defaultProps} />);

      expect(screen.getByText(/no recommendations yet/i)).toBeInTheDocument();
    });

    it('displays funnel with metrics when recommendations exist', () => {
      const propsWithMetrics = {
        ...defaultProps,
        funnel_metrics: {
          generated: 100,
          approved: 50,
          applied: 25,
        },
      };

      render(<Dashboard {...propsWithMetrics} />);

      expect(screen.getByText('Generated')).toBeInTheDocument();
      expect(screen.getByText('Approved')).toBeInTheDocument();
      expect(screen.getByText('Applied')).toBeInTheDocument();
    });
  });
});
