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

import { useSerpAnalysis } from './useSerpAnalysis';

vi.mock('axios');

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

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

  it('starts with idle status and no data', () => {
    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1 }));

    expect(result.current.status).toBe('idle');
    expect(result.current.serpSnapshotId).toBeNull();
    expect(result.current.error).toBeNull();
    expect(result.current.isAnalyzing).toBe(false);
    expect(result.current.serpData).toBeNull();
  });

  it('triggers analysis and sets pending status', async () => {
    vi.mocked(axios.post).mockResolvedValue({
      data: {
        serp_snapshot_id: 42,
        status: 'pending',
        message: 'Analysis started',
      },
    });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1 }));

    await act(async () => {
      result.current.analyze('best seo tools');
      await Promise.resolve();
    });

    expect(axios.post).toHaveBeenCalledWith(expect.any(String), {
      keyword: 'best seo tools',
      language_code: undefined,
      target_url: undefined,
    });
    expect(result.current.serpSnapshotId).toBe(42);
    expect(result.current.status).toBe('pending');
    expect(result.current.isAnalyzing).toBe(true);
  });

  it('handles immediately completed analysis', async () => {
    const mockData = {
      keyword: 'test',
      results: [],
      analysis: {
        avg_word_count: 1000,
        word_count_range: { min: 500, max: 1500 },
        common_h2_terms: [],
        top_terms: [],
      },
      nlp_terms: [],
      competitor_content: [],
    };

    vi.mocked(axios.post).mockResolvedValue({
      data: {
        serp_snapshot_id: 42,
        status: 'completed',
        message: 'Using cached results',
      },
    });
    vi.mocked(axios.get).mockResolvedValue({
      data: { status: 'completed', data: mockData, error: null },
    });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1 }));

    await act(async () => {
      result.current.analyze('test keyword');
      await Promise.resolve();
      await Promise.resolve();
    });

    expect(result.current.status).toBe('completed');
    expect(result.current.serpData).toEqual(mockData);
    expect(result.current.isAnalyzing).toBe(false);
  });

  it('polls for status when analysis is pending', async () => {
    vi.mocked(axios.post).mockResolvedValue({
      data: { serp_snapshot_id: 42, status: 'processing', message: 'Processing' },
    });
    vi.mocked(axios.get).mockResolvedValue({
      data: { status: 'processing', data: null, error: null },
    });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1, pollingIntervalMs: 1000 }));

    await act(async () => {
      result.current.analyze('test');
      await Promise.resolve();
    });

    expect(result.current.status).toBe('processing');
    expect(result.current.isAnalyzing).toBe(true);

    // Advance timer to trigger polling
    await act(async () => {
      vi.advanceTimersByTime(1000);
      await Promise.resolve();
    });

    expect(axios.get).toHaveBeenCalled();
  });

  it('stops polling when analysis completes', async () => {
    const mockData = {
      keyword: 'test',
      results: [],
      analysis: {
        avg_word_count: 1000,
        word_count_range: { min: 500, max: 1500 },
        common_h2_terms: [],
        top_terms: [],
      },
      nlp_terms: [],
      competitor_content: [],
    };

    vi.mocked(axios.post).mockResolvedValue({
      data: { serp_snapshot_id: 42, status: 'processing', message: 'Processing' },
    });
    vi.mocked(axios.get).mockResolvedValue({
      data: { status: 'completed', data: mockData, error: null },
    });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1, pollingIntervalMs: 1000 }));

    await act(async () => {
      result.current.analyze('test');
      await Promise.resolve();
    });

    // First poll
    await act(async () => {
      vi.advanceTimersByTime(1000);
      await Promise.resolve();
    });

    expect(result.current.status).toBe('completed');
    expect(result.current.serpData).toEqual(mockData);
    expect(result.current.isAnalyzing).toBe(false);

    // Advance more — should NOT poll again
    vi.mocked(axios.get).mockClear();
    await act(async () => {
      vi.advanceTimersByTime(3000);
      await Promise.resolve();
    });

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

  it('stops polling when analysis fails', async () => {
    vi.mocked(axios.post).mockResolvedValue({
      data: { serp_snapshot_id: 42, status: 'processing', message: 'Processing' },
    });
    vi.mocked(axios.get).mockResolvedValue({
      data: { status: 'failed', data: null, error: 'API credentials invalid' },
    });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1, pollingIntervalMs: 1000 }));

    await act(async () => {
      result.current.analyze('test');
      await Promise.resolve();
    });

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

    expect(result.current.status).toBe('failed');
    expect(result.current.error).toBe('API credentials invalid');
    expect(result.current.isAnalyzing).toBe(false);
  });

  it('handles trigger API error', async () => {
    vi.mocked(axios.post).mockRejectedValue({
      response: { data: { message: 'SERP API key not configured' } },
      isAxiosError: true,
    });
    vi.mocked(axios.isAxiosError).mockReturnValue(true);

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1 }));

    await act(async () => {
      result.current.analyze('test');
      await Promise.resolve();
    });

    expect(result.current.status).toBe('failed');
    expect(result.current.error).toBe('SERP API key not configured');
  });

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

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1 }));

    await act(async () => {
      result.current.analyze('test');
      await Promise.resolve();
    });

    expect(result.current.status).toBe('failed');
    expect(result.current.error).toBe('Failed to start SERP analysis');
  });

  it('cleans up polling on unmount', async () => {
    vi.mocked(axios.post).mockResolvedValue({
      data: { serp_snapshot_id: 42, status: 'processing', message: 'Processing' },
    });

    const { result, unmount } = renderHook(() =>
      useSerpAnalysis({ siteId: 1, pollingIntervalMs: 1000 }),
    );

    await act(async () => {
      result.current.analyze('test');
      await Promise.resolve();
    });

    unmount();

    // After unmount, no more polls should happen
    vi.mocked(axios.get).mockClear();
    await act(async () => {
      vi.advanceTimersByTime(5000);
    });

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

  it('times out after 120 seconds of polling', async () => {
    const startTime = 1000000;
    let currentTime = startTime;
    vi.spyOn(Date, 'now').mockImplementation(() => currentTime);

    vi.mocked(axios.post).mockResolvedValue({
      data: { serp_snapshot_id: 42, status: 'processing', message: 'Processing' },
    });
    vi.mocked(axios.get).mockResolvedValue({
      data: { status: 'processing', data: null, error: null },
    });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1, pollingIntervalMs: 1000 }));

    await act(async () => {
      result.current.analyze('test');
      await Promise.resolve();
    });

    expect(result.current.isAnalyzing).toBe(true);

    // Advance Date.now past 120s and trigger a poll
    currentTime = startTime + 121_000;
    await act(async () => {
      vi.advanceTimersByTime(1000);
      await Promise.resolve();
    });

    expect(result.current.status).toBe('failed');
    expect(result.current.error).toBe('Analysis timed out. Try again.');
    expect(result.current.isAnalyzing).toBe(false);
  });

  it('cancels analysis and resets state', async () => {
    vi.mocked(axios.post).mockResolvedValue({
      data: { serp_snapshot_id: 42, status: 'processing', message: 'Processing' },
    });
    vi.mocked(axios.get).mockResolvedValue({
      data: { status: 'processing', data: null, error: null },
    });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1, pollingIntervalMs: 1000 }));

    await act(async () => {
      result.current.analyze('test');
      await Promise.resolve();
    });

    expect(result.current.isAnalyzing).toBe(true);

    act(() => {
      result.current.cancel();
    });

    expect(result.current.status).toBe('idle');
    expect(result.current.error).toBeNull();
    expect(result.current.serpData).toBeNull();
    expect(result.current.serpSnapshotId).toBeNull();
    expect(result.current.isAnalyzing).toBe(false);

    // Verify polling stopped
    vi.mocked(axios.get).mockClear();
    await act(async () => {
      vi.advanceTimersByTime(5000);
    });

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

  it('resets state when analyzing new keyword', async () => {
    vi.mocked(axios.post)
      .mockResolvedValueOnce({
        data: { serp_snapshot_id: 42, status: 'processing', message: 'Processing' },
      })
      .mockResolvedValueOnce({
        data: { serp_snapshot_id: 43, status: 'pending', message: 'Started' },
      });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 1 }));

    // First analysis
    await act(async () => {
      result.current.analyze('keyword1');
      await Promise.resolve();
    });

    expect(result.current.serpSnapshotId).toBe(42);

    // Second analysis — should reset
    await act(async () => {
      result.current.analyze('keyword2');
      await Promise.resolve();
    });

    expect(result.current.serpSnapshotId).toBe(43);
    expect(result.current.serpData).toBeNull();
    expect(result.current.error).toBeNull();
  });

  it('passes optional parameters to API', async () => {
    vi.mocked(axios.post).mockResolvedValue({
      data: { serp_snapshot_id: 42, status: 'pending', message: 'Started' },
    });

    const { result } = renderHook(() => useSerpAnalysis({ siteId: 5 }));

    await act(async () => {
      result.current.analyze('test keyword', 'de', 'https://example.com/page');
      await Promise.resolve();
    });

    expect(axios.post).toHaveBeenCalledWith(expect.any(String), {
      keyword: 'test keyword',
      language_code: 'de',
      target_url: 'https://example.com/page',
    });
  });
});
