import { renderHook, act } from '@testing-library/react';
import axios from 'axios';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';

import { useContentScore } from './useContentScore';
import type { NlpTerm } from './useSerpAnalysis';

vi.mock('axios');

const mockNlpTerms: NlpTerm[] = [
  {
    term: 'seo optimization',
    term_normalized: 'seo optimization',
    tf_idf_score: 0.8,
    document_frequency: 5,
    avg_term_frequency: 0.002,
    importance_rank: 1,
    in_title: true,
    in_headings: true,
  },
  {
    term: 'search engine',
    term_normalized: 'search engine',
    tf_idf_score: 0.6,
    document_frequency: 4,
    avg_term_frequency: 0.003,
    importance_rank: 2,
    in_title: false,
    in_headings: true,
  },
];

const mockScoreResult = {
  overall_score: 72,
  term_coverage: { score: 80, covered: 8, total: 10, percentage: 80 },
  word_count_score: { score: 65, draft_count: 1200, competitor_avg: 1500, competitor_median: 1400 },
  structure_score: { score: 70, details: {} },
  readability_score: { score: 75, details: {} },
  terms: [
    {
      term: 'seo optimization',
      importance_rank: 1,
      tf_idf_score: 0.8,
      present: true,
      frequency: 3,
      competitor_avg_frequency: 0.002,
      in_title: true,
      in_headings: true,
      status: 'covered' as const,
    },
  ],
};

describe('useContentScore', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    vi.useFakeTimers();
  });

  afterEach(() => {
    vi.restoreAllMocks();
    vi.useRealTimers();
  });

  it('starts with null score and empty terms', () => {
    const { result } = renderHook(() =>
      useContentScore({
        content: '',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: null,
        nlpTerms: null,
      }),
    );

    expect(result.current.score).toBeNull();
    expect(result.current.isLoading).toBe(false);
    expect(result.current.error).toBeNull();
    expect(result.current.terms).toEqual([]);
  });

  it('does not fetch score when serpSnapshotId is null', async () => {
    renderHook(() =>
      useContentScore({
        content: '<p>Test content</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: null,
        nlpTerms: mockNlpTerms,
      }),
    );

    await act(async () => {
      vi.advanceTimersByTime(3000);
      await Promise.resolve();
    });

    expect(axios.post).not.toHaveBeenCalled();
  });

  it('does not fetch score when content is empty', async () => {
    renderHook(() =>
      useContentScore({
        content: '',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: 42,
        nlpTerms: mockNlpTerms,
      }),
    );

    await act(async () => {
      vi.advanceTimersByTime(3000);
      await Promise.resolve();
    });

    expect(axios.post).not.toHaveBeenCalled();
  });

  it('performs client-side term matching instantly', () => {
    const { result } = renderHook(() =>
      useContentScore({
        content: '<p>Learn about seo optimization techniques and search engine best practices.</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: null,
        nlpTerms: mockNlpTerms,
      }),
    );

    expect(result.current.terms).toHaveLength(2);

    const seoTerm = result.current.terms.find((t) => t.term === 'seo optimization');
    expect(seoTerm).toBeDefined();
    expect(seoTerm?.present).toBe(true);
    expect(seoTerm?.frequency).toBeGreaterThan(0);

    const searchTerm = result.current.terms.find((t) => t.term === 'search engine');
    expect(searchTerm).toBeDefined();
    expect(searchTerm?.present).toBe(true);
  });

  it('detects missing terms in client-side matching', () => {
    const { result } = renderHook(() =>
      useContentScore({
        content: '<p>This content has no relevant terms.</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: null,
        nlpTerms: mockNlpTerms,
      }),
    );

    const seoTerm = result.current.terms.find((t) => t.term === 'seo optimization');
    expect(seoTerm?.status).toBe('missing');
    expect(seoTerm?.present).toBe(false);
    expect(seoTerm?.frequency).toBe(0);
  });

  it('debounces server-side scoring', async () => {
    vi.mocked(axios.post).mockResolvedValue({ data: mockScoreResult });

    const { rerender } = renderHook(
      ({ content }) =>
        useContentScore({
          content,
          siteId: 1,
          siteUrl: 'https://example.com',
          serpSnapshotId: 42,
          nlpTerms: mockNlpTerms,
          debounceMs: 2000,
        }),
      { initialProps: { content: '<p>Test 1</p>' } },
    );

    // Should not call immediately
    expect(axios.post).not.toHaveBeenCalled();

    // Update content before debounce expires
    rerender({ content: '<p>Test 2</p>' });
    rerender({ content: '<p>Test 3</p>' });

    // Advance past debounce
    await act(async () => {
      vi.advanceTimersByTime(2000);
      await Promise.resolve();
    });

    // Should only call once with the latest content
    expect(axios.post).toHaveBeenCalledTimes(1);
  });

  it('fetches server-side score successfully', async () => {
    vi.mocked(axios.post).mockResolvedValue({ data: mockScoreResult });

    const { result } = renderHook(() =>
      useContentScore({
        content: '<p>SEO optimization content</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: 42,
        nlpTerms: mockNlpTerms,
        debounceMs: 500,
      }),
    );

    await act(async () => {
      vi.advanceTimersByTime(500);
      await Promise.resolve();
    });

    expect(result.current.score).toEqual(mockScoreResult);
    expect(result.current.isLoading).toBe(false);
    expect(result.current.error).toBeNull();
  });

  it('uses server terms when available over local terms', async () => {
    vi.mocked(axios.post).mockResolvedValue({ data: mockScoreResult });

    const { result } = renderHook(() =>
      useContentScore({
        content: '<p>SEO optimization content</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: 42,
        nlpTerms: mockNlpTerms,
        debounceMs: 500,
      }),
    );

    // Before server response, uses local terms
    expect(result.current.terms).toHaveLength(2);

    await act(async () => {
      vi.advanceTimersByTime(500);
      await Promise.resolve();
    });

    // After server response, uses server terms
    expect(result.current.terms).toEqual(mockScoreResult.terms);
  });

  it('handles API errors gracefully', async () => {
    vi.mocked(axios.post).mockRejectedValue({
      response: { data: { message: 'SERP analysis is not yet complete.' } },
      isAxiosError: true,
    });
    vi.mocked(axios.isAxiosError).mockReturnValue(true);

    const { result } = renderHook(() =>
      useContentScore({
        content: '<p>Test content</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: 42,
        nlpTerms: mockNlpTerms,
        debounceMs: 500,
      }),
    );

    await act(async () => {
      vi.advanceTimersByTime(500);
      await Promise.resolve();
    });

    expect(result.current.score).toBeNull();
    expect(result.current.isLoading).toBe(false);
    expect(result.current.error).toBe('SERP analysis is not yet complete.');
  });

  it('handles network errors gracefully', async () => {
    vi.mocked(axios.post).mockRejectedValue(new Error('Network error'));
    vi.mocked(axios.isAxiosError).mockReturnValue(false);

    const { result } = renderHook(() =>
      useContentScore({
        content: '<p>Test content</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: 42,
        nlpTerms: mockNlpTerms,
        debounceMs: 500,
      }),
    );

    await act(async () => {
      vi.advanceTimersByTime(500);
      await Promise.resolve();
    });

    expect(result.current.error).toBe('Failed to score content');
  });

  it('returns empty terms when nlpTerms is null', () => {
    const { result } = renderHook(() =>
      useContentScore({
        content: '<p>Some content</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: null,
        nlpTerms: null,
      }),
    );

    expect(result.current.terms).toEqual([]);
  });

  it('returns empty terms when content is whitespace only', () => {
    const { result } = renderHook(() =>
      useContentScore({
        content: '   ',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: null,
        nlpTerms: mockNlpTerms,
      }),
    );

    expect(result.current.terms).toEqual([]);
  });

  it('strips HTML tags during client-side matching', () => {
    const { result } = renderHook(() =>
      useContentScore({
        content: '<h1>SEO</h1><p><strong>optimization</strong> guide</p>',
        siteId: 1,
        siteUrl: 'https://example.com',
        serpSnapshotId: null,
        nlpTerms: [
          {
            term: 'seo',
            term_normalized: 'seo',
            tf_idf_score: 0.5,
            document_frequency: 3,
            avg_term_frequency: 0.001,
            importance_rank: 1,
            in_title: false,
            in_headings: false,
          },
        ],
      }),
    );

    const seoTerm = result.current.terms.find((t) => t.term === 'seo');
    expect(seoTerm?.present).toBe(true);
  });
});
