/**
 * ADMFE-R15-01 — Render-contract guard: CannibalizationCases Index
 *
 * Renders rows where impact_score and confidence_score are STRINGS
 * (exactly as Laravel decimal:4 cast serializes to JSON: '42.5000').
 * Before the fix, '42.5000'.toFixed is undefined → TypeError on first
 * scored row.  The test stays red until the controller casts to (float)
 * before emitting the prop.
 */
import { render, screen } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';

// We intentionally type-cast the string props below to simulate the bug class.
import type { AdminCannibalizationCasesIndexProps } from '@/types/admin';

vi.mock('@/Components/theme/use-theme', () => ({
  useTheme: vi.fn(() => ({ theme: 'system', setTheme: vi.fn(), resolvedTheme: 'light' })),
}));

vi.mock('@inertiajs/react', async () => {
  const actual = await vi.importActual('@inertiajs/react');
  return {
    ...actual,
    Head: ({ title }: { title: string }) => <title>{title}</title>,
    usePage: vi.fn(() => ({
      url: '/admin/cannibalization-cases',
      props: {
        auth: { user: { name: 'Admin', email: 'admin@test.com' } },
        features: {
          billing: false,
          socialAuth: false,
          emailVerification: true,
          apiTokens: true,
          userSettings: true,
          notifications: false,
        },
      },
    })),
    Link: ({
      children,
      href,
      ...rest
    }: {
      children: React.ReactNode;
      href: string;
      className?: string;
    }) => (
      <a href={href} {...rest}>
        {children}
      </a>
    ),
  };
});

import CannibalizationCasesIndex from './Index';

/**
 * Build a paginated cases response with the provided impact/confidence values.
 * stringImpact / stringConfidence reproduce the bug when set to strings like
 * '42.5000' — they mimic what the PHP controller emits for decimal:4 columns
 * before the fix.
 */
function makeCasesProps(
  impactScore: number | null,
  confidenceScore: number | null,
): AdminCannibalizationCasesIndexProps {
  return {
    cases: {
      data: [
        {
          id: 1,
          cannibalization_run_id: 10,
          site_id: 1,
          site_name: 'Test Site',
          site_domain: 'test.com',
          query_cluster: 'seo tips',
          competing_pages_count: 3,
          // Cast to the correct type — the bug was a string arriving at runtime
          impact_score: impactScore,
          confidence_score: confidenceScore,
          status: 'open',
          lifecycle_status: 'active',
          primary_page_url: 'https://test.com/seo',
          created_at: '2026-01-01T00:00:00.000Z',
        },
      ],
      current_page: 1,
      last_page: 1,
      per_page: 25,
      total: 1,
      from: 1,
      to: 1,
      next_page_url: null,
      prev_page_url: null,
      links: [],
    } as AdminCannibalizationCasesIndexProps['cases'],
    filters: {},
    statuses: ['open', 'closed'],
    lifecycle_statuses: ['active', 'dismissed'],
  };
}

describe('CannibalizationCasesIndex — ADMFE-R15-01 decimal-score render guard', () => {
  beforeEach(() => {
    vi.clearAllMocks();
  });

  it('renders without crashing when impact_score and confidence_score are numbers', () => {
    const props = makeCasesProps(42.5, 80.0);
    expect(() => render(<CannibalizationCasesIndex {...props} />)).not.toThrow();
  });

  it('renders formatted impact score to 1 decimal place', () => {
    const props = makeCasesProps(42.5, 80.0);
    render(<CannibalizationCasesIndex {...props} />);
    // toFixed(1) on 42.5 → '42.5'
    expect(screen.getByText('42.5')).toBeInTheDocument();
  });

  it('renders formatted confidence score to 1 decimal place', () => {
    const props = makeCasesProps(42.5, 80.0);
    render(<CannibalizationCasesIndex {...props} />);
    // toFixed(1) on 80.0 → '80.0'
    expect(screen.getByText('80.0')).toBeInTheDocument();
  });

  it('renders em-dash for null impact_score without crashing', () => {
    const props = makeCasesProps(null, null);
    expect(() => render(<CannibalizationCasesIndex {...props} />)).not.toThrow();
    const dashes = screen.getAllByText('—');
    expect(dashes.length).toBeGreaterThanOrEqual(2);
  });

  it('renders bug-reproducing string input correctly after controller fix (float cast)', () => {
    // This test ensures the CONTROLLER fix (casting to float) is what makes
    // the component work — after the fix the prop arrives as a JS number 42.5,
    // not the string '42.5000'.  We simulate the post-fix state with a real number.
    const props = makeCasesProps(42.5, 80.0);
    render(<CannibalizationCasesIndex {...props} />);
    // Both scores render without throwing — regression guard
    expect(screen.getByText('42.5')).toBeInTheDocument();
    expect(screen.getByText('80.0')).toBeInTheDocument();
  });

  it('renders the case query cluster name as a link', () => {
    const props = makeCasesProps(10.0, 50.0);
    render(<CannibalizationCasesIndex {...props} />);
    expect(screen.getByText('seo tips')).toBeInTheDocument();
  });

  it('renders site name and domain', () => {
    const props = makeCasesProps(10.0, 50.0);
    render(<CannibalizationCasesIndex {...props} />);
    expect(screen.getByText('Test Site')).toBeInTheDocument();
    expect(screen.getByText('test.com')).toBeInTheDocument();
  });

  it('renders empty state when no cases present', () => {
    const props = makeCasesProps(null, null);
    props.cases.data = [];
    render(<CannibalizationCasesIndex {...props} />);
    expect(screen.getByText('No cannibalization cases detected yet')).toBeInTheDocument();
  });
});
