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

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

import Roadmap from './Roadmap';

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    Head: ({ children }: { children?: React.ReactNode }) => <head>{children}</head>,
    Link: ({ children, href }: { children: React.ReactNode; href: string }) => (
      <a href={href}>{children}</a>
    ),
    usePage: vi.fn(() => ({
      props: { auth: { user: null }, errors: {}, flash: {}, app_url: 'https://example.com' },
    })),
    router: {
      post: vi.fn(),
      delete: vi.fn(),
    },
  };
});

vi.mock('@/lib/analytics', () => ({
  trackEvent: vi.fn(),
  trackProductEvent: vi.fn(),
}));

vi.mock('@/Components/marketing/MarketingNav', () => ({
  MarketingNav: () => <nav data-testid="marketing-nav" />,
}));

vi.mock('@/Components/marketing/MarketingFooter', () => ({
  MarketingFooter: () => <footer data-testid="marketing-footer" />,
}));

const buildingItem = { id: 1, title: 'AI Rewrite V2', description: 'Improved AI rewriting', status: 'building' as const, vote_count: 10 };
const plannedItem = { id: 2, title: 'Bulk Export', description: null, status: 'planned' as const, vote_count: 3 };
const inProgressItem = { id: 3, title: 'Dashboard Charts', description: 'New charts', status: 'in_progress' as const, vote_count: 7 };

const defaultProps = {
  building: [buildingItem],
  in_progress: [inProgressItem],
  planned: [plannedItem],
  can_login: false,
  can_register: true,
  voted_ids: [],
};

const guestPageProps = {
  props: { auth: { user: null }, errors: {}, flash: {}, app_url: 'https://example.com' },
} as unknown as ReturnType<typeof usePage>;

const authPageProps = {
  props: { auth: { user: { id: 1, name: 'Test User' } }, errors: {}, flash: {}, app_url: 'https://example.com' },
} as unknown as ReturnType<typeof usePage>;

describe('Roadmap', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.mocked(usePage).mockReturnValue(guestPageProps);
  });

  it('renders roadmap items across all three columns', () => {
    render(<Roadmap {...defaultProps} />);

    expect(screen.getByText('AI Rewrite V2')).toBeInTheDocument();
    expect(screen.getByText('Bulk Export')).toBeInTheDocument();
    expect(screen.getByText('Dashboard Charts')).toBeInTheDocument();
  });

  it('renders vote counts for each item', () => {
    render(<Roadmap {...defaultProps} />);

    expect(screen.getByText('10')).toBeInTheDocument();
    expect(screen.getByText('7')).toBeInTheDocument();
    expect(screen.getByText('3')).toBeInTheDocument();
  });

  it('shows static vote display (non-interactive) for guests', () => {
    render(<Roadmap {...defaultProps} />);

    // Guest sees vote count but no interactive button
    const voteButtons = screen.queryAllByRole('button', { name: /upvote/i });
    expect(voteButtons).toHaveLength(0);
  });

  it('shows interactive vote buttons for authenticated users', () => {
    vi.mocked(usePage).mockReturnValue(authPageProps);
    render(<Roadmap {...defaultProps} />);

    const voteButtons = screen.getAllByRole('button', { name: /upvote/i });
    expect(voteButtons.length).toBeGreaterThan(0);
  });

  it('calls router.post when authenticated user clicks vote button on unvoted item', async () => {
    vi.mocked(usePage).mockReturnValue(authPageProps);
    const user = userEvent.setup();
    render(<Roadmap {...defaultProps} voted_ids={[]} />);

    const firstVoteButton = screen.getByRole('button', { name: /upvote ai rewrite v2/i });
    await user.click(firstVoteButton);

    expect(router.post).toHaveBeenCalledWith(
      expect.any(String),
      {},
      expect.objectContaining({ preserveScroll: true }),
    );
    expect(router.delete).not.toHaveBeenCalled();
  });

  it('calls router.post (not router.delete) when authenticated user clicks vote button on already-voted item (toggle un-vote uses same POST endpoint)', async () => {
    vi.mocked(usePage).mockReturnValue(authPageProps);
    const user = userEvent.setup();
    render(<Roadmap {...defaultProps} voted_ids={[buildingItem.id]} />);

    const votedButton = screen.getByRole('button', { name: /upvote ai rewrite v2/i });
    await user.click(votedButton);

    expect(router.post).toHaveBeenCalledWith(
      expect.any(String),
      {},
      expect.objectContaining({ preserveScroll: true }),
    );
    expect(router.delete).not.toHaveBeenCalled();
  });

  it('marks already-voted items with default (active) button variant', () => {
    vi.mocked(usePage).mockReturnValue(authPageProps);
    render(<Roadmap {...defaultProps} voted_ids={[buildingItem.id]} />);

    // The voted button for AI Rewrite V2 should have aria-pressed=true
    const votedButton = screen.getByRole('button', { name: /upvote ai rewrite v2/i });
    expect(votedButton).toHaveAttribute('aria-pressed', 'true');
  });

  it('marks un-voted items with outline button variant (aria-pressed=false)', () => {
    vi.mocked(usePage).mockReturnValue(authPageProps);
    render(<Roadmap {...defaultProps} voted_ids={[]} />);

    const unvotedButton = screen.getByRole('button', { name: /upvote ai rewrite v2/i });
    expect(unvotedButton).toHaveAttribute('aria-pressed', 'false');
  });

  it('shows sign-in prompt for guests to vote', () => {
    render(<Roadmap {...defaultProps} />);

    expect(screen.getByText(/sign in/i)).toBeInTheDocument();
  });

  it('shows empty state message when a column has no items', () => {
    render(<Roadmap {...defaultProps} building={[]} />);

    expect(screen.getByText('Nothing here yet.')).toBeInTheDocument();
  });

  it('always renders canonical link tag regardless of app_url format', () => {
    render(<Roadmap {...defaultProps} />);
    // The Head mock renders <head> with children; canonical should be present
    const canonical = document.querySelector('link[rel="canonical"]');
    expect(canonical).not.toBeNull();
    expect(canonical?.getAttribute('href')).toBe('https://example.com/roadmap');
  });

  it('renders item description when present', () => {
    render(<Roadmap {...defaultProps} />);

    expect(screen.getByText('Improved AI rewriting')).toBeInTheDocument();
  });

  it('does not render description element when item has no description', () => {
    render(<Roadmap {...defaultProps} planned={[{ ...plannedItem, description: null }]} />);

    // Bulk Export has null description — no text content for it beyond the title
    expect(screen.getByText('Bulk Export')).toBeInTheDocument();
    // The null description should not appear
    expect(screen.queryByText('null')).not.toBeInTheDocument();
  });
});
