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

import { competitors as competitorConfigs } from '@/data/competitors';

import Comparison from './Comparison';

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

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

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

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

describe('Comparison — schema markup (SEO-005)', () => {
  beforeEach(() => {
    Object.defineProperty(window, 'location', {
      value: { origin: 'https://example.com' },
      writable: true,
    });
  });

  const renderPage = () =>
    render(<Comparison canLogin={true} canRegister={true} competitor="surfer-seo" />);

  it('renders at least 4 JSON-LD script tags', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    expect(scripts.length).toBeGreaterThanOrEqual(4);
  });

  it('includes a WebPage schema', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    const schemas = Array.from(scripts).map(
      (s) => JSON.parse(s.textContent || '{}') as Record<string, unknown>,
    );
    const webPage = schemas.find((s) => s['@type'] === 'WebPage');
    expect(webPage).toBeDefined();
    expect(webPage?.['@context']).toBe('https://schema.org');
  });

  it('WebPage schema references both products via about', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    const schemas = Array.from(scripts).map(
      (s) => JSON.parse(s.textContent || '{}') as Record<string, unknown>,
    );
    const webPage = schemas.find((s) => s['@type'] === 'WebPage');
    const about = webPage?.['about'] as Array<Record<string, unknown>>;
    expect(Array.isArray(about)).toBe(true);
    expect(about).toHaveLength(2);
    expect(about[0]['@type']).toBe('SoftwareApplication');
    expect(about[1]['@type']).toBe('SoftwareApplication');
  });

  it('includes a BreadcrumbList schema', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    const schemas = Array.from(scripts).map(
      (s) => JSON.parse(s.textContent || '{}') as Record<string, unknown>,
    );
    const breadcrumb = schemas.find((s) => s['@type'] === 'BreadcrumbList');
    expect(breadcrumb).toBeDefined();
    const items = breadcrumb?.['itemListElement'] as Array<Record<string, unknown>>;
    expect(items).toHaveLength(3);
    expect(items[0]['name']).toBe('Home');
    expect(items[1]['name']).toBe('Compare');
    expect(items[1]['item'] as string).toContain('/compare');
    expect(items[2]['name']).toContain('Surfer SEO');
  });

  it('includes a SoftwareApplication schema for RankWiz', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    const schemas = Array.from(scripts).map(
      (s) => JSON.parse(s.textContent || '{}') as Record<string, unknown>,
    );
    const softwareApp = schemas.find((s) => s['@type'] === 'SoftwareApplication');
    expect(softwareApp).toBeDefined();
    expect(softwareApp?.['@context']).toBe('https://schema.org');
    expect(softwareApp?.['applicationCategory']).toBe('BusinessApplication');
    expect(softwareApp?.['operatingSystem']).toBe('Web');
  });

  it('SoftwareApplication schema includes an Offer', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    const schemas = Array.from(scripts).map(
      (s) => JSON.parse(s.textContent || '{}') as Record<string, unknown>,
    );
    const softwareApp = schemas.find((s) => s['@type'] === 'SoftwareApplication');
    const offer = softwareApp?.['offers'] as Record<string, unknown>;
    expect(offer).toBeDefined();
    expect(offer['@type']).toBe('Offer');
    expect(offer['priceCurrency']).toBe('USD');
  });

  it('includes an ItemList schema for the feature comparison table', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    const schemas = Array.from(scripts).map(
      (s) => JSON.parse(s.textContent || '{}') as Record<string, unknown>,
    );
    const itemList = schemas.find((s) => s['@type'] === 'ItemList');
    expect(itemList).toBeDefined();
    expect(itemList?.['@context']).toBe('https://schema.org');
    const items = itemList?.['itemListElement'] as Array<Record<string, unknown>>;
    expect(Array.isArray(items)).toBe(true);
    expect(items?.length ?? 0).toBeGreaterThan(0);
    expect(items[0]['@type']).toBe('ListItem');
    expect(items[0]['position']).toBe(1);
  });

  it('uses competitor-specific meta description and per-page OG image', () => {
    const { container } = renderPage();
    const metaDesc = container.querySelector('meta[name="description"]');
    const ogImage = container.querySelector('meta[property="og:image"]');
    expect(metaDesc?.getAttribute('content')).toContain('Surfer SEO');
    // Each comparison page has its own OG image based on ogImageSlug
    expect(ogImage?.getAttribute('content')).toBe('/og-vs-surfer-seo.svg');
  });

  it('all JSON-LD script tags contain valid JSON', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    scripts.forEach((script) => {
      expect(() => JSON.parse(script.textContent || '')).not.toThrow();
    });
  });

  it('renders comparison page for each supported competitor', () => {
    Object.keys(competitorConfigs).forEach((slug) => {
      const { container } = render(
        <Comparison canLogin={true} canRegister={true} competitor={slug} />,
      );
      const scripts = container.querySelectorAll('script[type="application/ld+json"]');
      expect(scripts.length).toBeGreaterThanOrEqual(4);
    });
  });

  it('JSON-LD script blocks do not contain raw </script> sequences (XSS guard)', () => {
    const { container } = renderPage();
    const scripts = container.querySelectorAll('script[type="application/ld+json"]');
    scripts.forEach((script) => {
      // textContent is what the browser would render into the script block.
      // It must not contain a literal </script closing tag — that would allow
      // an attacker-controlled string in any field to break out of the block.
      expect(script.textContent).not.toContain('</script');
    });
  });

  it('returns null for unknown competitor slug', () => {
    const { container } = render(
      <Comparison canLogin={true} canRegister={true} competitor="unknown-tool" />,
    );
    // Should render nothing (null return)
    expect(container.firstChild).toBeNull();
  });

  it('renders lastVerified as a formatted month + year string, not raw ISO date', () => {
    const { getByText } = render(
      <Comparison canLogin={true} canRegister={true} competitor="surfer-seo" />,
    );
    // Should display "March 2026", not "2026-03-01"
    expect(getByText(/Feature data last verified March 2026/)).toBeTruthy();
    expect(() => getByText(/2026-03-01/)).toThrow();
  });
});
