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

import WhyThisRewrite from '@/Components/Recommendations/WhyThisRewrite';

describe('WhyThisRewrite', () => {
  it('renders top evidence factors from the recommendation evidence', () => {
    render(
      <WhyThisRewrite
        actionType="content_rewrite"
        evidence={{
          clicks_before: 1200,
          clicks_after: 800,
          delta_percent: -33.3,
          position_before: 4,
        }}
        dataQuality={{ sufficient: true, coverage: 'high', missing: [] }}
      />,
    );

    expect(screen.getByText('Top evidence')).toBeInTheDocument();
    expect(screen.getByText('Clicks before')).toBeInTheDocument();
    expect(screen.getByText((1200).toLocaleString())).toBeInTheDocument();
    expect(screen.getByText('Clicks after')).toBeInTheDocument();
    expect(screen.getByText('Position before')).toBeInTheDocument();
  });

  it('renders the rewrite mode derived from the action type', () => {
    render(
      <WhyThisRewrite
        actionType="section_refresh"
        evidence={{ clicks_before: 100, clicks_after: 50 }}
        dataQuality={{ sufficient: true, coverage: 'high' }}
      />,
    );

    expect(screen.getByText('Rewrite mode')).toBeInTheDocument();
    expect(screen.getByText('Section refresh')).toBeInTheDocument();
  });

  it('renders the data-health badge when stale', () => {
    render(
      <WhyThisRewrite
        actionType="content_rewrite"
        evidence={{ clicks_before: 100, clicks_after: 50 }}
        dataQuality={{ sufficient: true, coverage: 'high' }}
        dataHealth="stale"
        dataHealthReasons={['GSC synced 9 days ago']}
      />,
    );

    expect(screen.getByText('Stale')).toBeInTheDocument();
  });

  it('shows the conservative-edit warning when data_quality.sufficient is false', () => {
    render(
      <WhyThisRewrite
        actionType="content_rewrite"
        evidence={{ clicks_before: 100, clicks_after: 50 }}
        dataQuality={{ sufficient: false, coverage: 'medium', missing: ['finding'] }}
      />,
    );

    expect(screen.getByTestId('conservative-edit-warning')).toBeInTheDocument();
    expect(screen.getByTestId('conservative-edit-warning')).toHaveClass(
      'bg-warning-soft',
      'text-warning-strong',
    );
  });

  it('shows the conservative-edit warning when coverage is low', () => {
    render(
      <WhyThisRewrite
        actionType="content_rewrite"
        evidence={{ clicks_before: 100 }}
        dataQuality={{ sufficient: true, coverage: 'low' }}
      />,
    );

    expect(screen.getByTestId('conservative-edit-warning')).toBeInTheDocument();
  });

  it('shows the conservative-edit warning when there are no usable evidence factors', () => {
    render(<WhyThisRewrite actionType="content_rewrite" evidence={{}} />);

    expect(screen.getByText('Limited evidence available for this page.')).toBeInTheDocument();
    expect(screen.getByTestId('conservative-edit-warning')).toBeInTheDocument();
  });

  it('does not show the conservative warning when evidence is sufficient', () => {
    render(
      <WhyThisRewrite
        actionType="content_rewrite"
        evidence={{ clicks_before: 1000, clicks_after: 600, delta_percent: -40 }}
        dataQuality={{ sufficient: true, coverage: 'high' }}
      />,
    );

    expect(screen.queryByTestId('conservative-edit-warning')).not.toBeInTheDocument();
  });

  it('renders without NaN/undefined when optional supporting_data evidence is absent', () => {
    // Item #2 (PLAN Open Assumptions #2 / freshness phantom-column repair):
    // a freshness recommendation whose supporting_data fields were omitted
    // resolves to null/undefined/NaN values on the evidence object. The card
    // must render those rows as ABSENT — never surface the literal text
    // "NaN" or "undefined" to the operator. formatFactorValue is the guard;
    // this asserts the user-visible outcome of that guard.
    // Every key below IS in the component's TOP_FACTOR_KEYS allowlist, so each
    // WOULD render a row if its value passed the finite/non-empty guard. That is
    // what makes the omission assertions meaningful (not tautological): the only
    // reason these rows are absent is formatFactorValue() rejecting NaN/undefined/
    // null. decay_magnitude is the one finite value that must still surface.
    const { container } = render(
      <WhyThisRewrite
        actionType="full_rewrite"
        evidence={{
          // Real, finite value — should render.
          decay_magnitude: 42,
          // Absent / non-finite optional supporting_data-derived fields, all of
          // which are TOP_FACTOR_KEYS members that would otherwise render.
          freshness_score: Number.NaN,
          clicks_before: undefined,
          clicks_after: null,
          position_before: Number.NaN,
        }}
        dataHealth="partial"
      />,
    );

    const text = container.textContent ?? '';
    expect(text).not.toContain('NaN');
    expect(text).not.toContain('undefined');
    // The one finite factor is still surfaced.
    expect(screen.getByText('Decay magnitude')).toBeInTheDocument();
    expect(screen.getByText((42).toLocaleString())).toBeInTheDocument();
    // The non-finite/absent factors are omitted, not rendered as broken rows.
    // Each label below corresponds to a TOP_FACTOR_KEY, so its absence proves
    // the value guard fired rather than the key simply being unlisted.
    expect(screen.queryByText('Freshness score')).not.toBeInTheDocument();
    expect(screen.queryByText('Clicks before')).not.toBeInTheDocument();
    expect(screen.queryByText('Clicks after')).not.toBeInTheDocument();
    expect(screen.queryByText('Position before')).not.toBeInTheDocument();
  });

  it('renders without crashing when evidence is null', () => {
    // The freshness fetcher omits the whole evidence object when no signal
    // data is available; the card must degrade to the limited-evidence path
    // rather than throwing or printing undefined.
    const { container } = render(
      <WhyThisRewrite actionType="content_rewrite" evidence={null} />,
    );

    expect(screen.getByText('Limited evidence available for this page.')).toBeInTheDocument();
    expect(container.textContent ?? '').not.toContain('undefined');
    expect(container.textContent ?? '').not.toContain('NaN');
  });

  it('prefers structured draftEvidence factors over the legacy evidence blob', () => {
    // When the structured prompt-evidence payload is present, the panel should
    // surface its bucketed factors (freshness/content-quality signals the raw
    // `evidence` blob does not carry), mirroring what the draft prompt sees.
    render(
      <WhyThisRewrite
        actionType="full_rewrite"
        evidence={{ clicks_before: 10, clicks_after: 5 }}
        draftEvidence={{
          clicks_before: 1200,
          clicks_after: 800,
          delta_percent: -33.3,
          query_evidence: { position_before: 4, position_after: 9 },
          intent_evidence: {},
          freshness_evidence: { decay_pattern_type: 'gradual', days_since_update: 410 },
          content_quality_evidence: { word_count: 642, h2_count: 3 },
          data_quality: { sufficient: true, coverage: 'high', missing: [] },
        }}
      />,
    );

    expect(screen.getByText('Top evidence')).toBeInTheDocument();
    // Structured factors are surfaced...
    expect(screen.getByText('Click change')).toBeInTheDocument();
    // ...and the flat metric reflects the structured payload (1,200), NOT the
    // legacy `evidence` blob value (10).
    expect(screen.getByText((1200).toLocaleString())).toBeInTheDocument();
    expect(screen.queryByText((10).toLocaleString())).not.toBeInTheDocument();
  });

  it('falls back to the legacy evidence display when draftEvidence is absent', () => {
    render(
      <WhyThisRewrite
        actionType="content_rewrite"
        evidence={{ clicks_before: 1200, clicks_after: 800, delta_percent: -33.3 }}
        dataQuality={{ sufficient: true, coverage: 'high', missing: [] }}
      />,
    );

    expect(screen.getByText('Clicks before')).toBeInTheDocument();
    expect(screen.getByText((1200).toLocaleString())).toBeInTheDocument();
  });

  it('uses draftEvidence.data_quality to drive the conservative-edit warning', () => {
    render(
      <WhyThisRewrite
        actionType="content_rewrite"
        evidence={{ clicks_before: 1000, clicks_after: 600, delta_percent: -40 }}
        // Loose prop says sufficient, but the canonical structured payload says
        // insufficient — the structured signal must win.
        dataQuality={{ sufficient: true, coverage: 'high' }}
        draftEvidence={{
          clicks_before: 1000,
          clicks_after: 600,
          delta_percent: -40,
          data_quality: { sufficient: false, coverage: 'low', missing: ['finding'] },
        }}
      />,
    );

    expect(screen.getByTestId('conservative-edit-warning')).toBeInTheDocument();
  });

  it('renders without crashing when draftEvidence is null', () => {
    const { container } = render(
      <WhyThisRewrite
        actionType="content_rewrite"
        evidence={{ clicks_before: 100, clicks_after: 50 }}
        draftEvidence={null}
      />,
    );

    expect(screen.getByText('Clicks before')).toBeInTheDocument();
    expect(container.textContent ?? '').not.toContain('undefined');
    expect(container.textContent ?? '').not.toContain('NaN');
  });

  it('renders no unsupported AI-search promises in its copy', () => {
    const { container } = render(
      <WhyThisRewrite
        actionType="geo_improvement"
        evidence={{ clicks_before: 100, clicks_after: 50 }}
        dataQuality={{ sufficient: false, coverage: 'low' }}
        dataHealth="partial"
      />,
    );

    const text = (container.textContent ?? '').toLowerCase();
    expect(text).not.toContain('rank in ai');
    expect(text).not.toContain('ai overview hack');
    expect(text).not.toContain('ai overview');
  });
});
