/**
 * History Feed Store Tests
 * Story 3.1: Testing history store state management
 */

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { renderHook, act, waitFor } from '@testing-library/react';
import { useHistoryStore } from './history-store';
import { DraftService } from '../db/draft-service';
import { db } from '../db';

// Mock DraftService methods
vi.mock('../db/draft-service', () => ({
  DraftService: {
    getCompletedDrafts: vi.fn(),
    getDraftById: vi.fn()
  }
}));

const mockDraft = {
  id: 1,
  sessionId: 'test-session',
  title: 'Test Draft',
  content: 'Test content',
  tags: ['test'],
  createdAt: Date.now(),
  completedAt: Date.now(),
  status: 'completed' as const
};

describe('HistoryStore', () => {
  beforeEach(async () => {
    vi.clearAllMocks();

    // Setup default mocks
    vi.mocked(DraftService.getCompletedDrafts).mockResolvedValue([]);

    // Clear database before each test
    await db.delete();
    await db.open();
  });

  afterEach(async () => {
    await db.delete();
  });

  describe('initial state', () => {
    it('has correct initial state', () => {
      const { result } = renderHook(() => useHistoryStore());

      expect(result.current.drafts).toEqual([]);
      expect(result.current.selectedDraft).toBeNull();
      expect(result.current.loading).toBe(false);
      expect(result.current.hasMore).toBe(true);
      expect(result.current.error).toBeNull();
    });
  });

  describe('loadMore', () => {
    it('loads drafts and appends to existing drafts', async () => {
      const drafts = Array(20).fill(null).map((_, i) => ({
        ...mockDraft,
        id: i + 1
      }));

      vi.mocked(DraftService.getCompletedDrafts).mockResolvedValue(drafts);

      const { result } = renderHook(() => useHistoryStore());

      await act(async () => {
        await result.current.loadMore();
      });

      expect(result.current.drafts).toHaveLength(20);
      expect(result.current.loading).toBe(false);
      expect(result.current.hasMore).toBe(true);
      expect(DraftService.getCompletedDrafts).toHaveBeenCalledWith({
        limit: 20,
        offset: 0
      });
    });

    it('sets hasMore to false when fewer drafts than PAGE_SIZE are returned', async () => {
      const drafts = Array(5).fill(null).map((_, i) => ({
        ...mockDraft,
        id: i + 1
      }));

      vi.mocked(DraftService.getCompletedDrafts).mockResolvedValue(drafts);

      const { result } = renderHook(() => useHistoryStore());

      await act(async () => {
        await result.current.loadMore();
      });

      expect(result.current.hasMore).toBe(false);
    });

    it('appends drafts to existing list for pagination', async () => {
      // First load
      const firstBatch = Array(20).fill(null).map((_, i) => ({
        ...mockDraft,
        id: i + 1
      }));

      // Second load
      const secondBatch = Array(10).fill(null).map((_, i) => ({
        ...mockDraft,
        id: i + 21
      }));

      vi.mocked(DraftService.getCompletedDrafts)
        .mockResolvedValueOnce(firstBatch)
        .mockResolvedValueOnce(secondBatch);

      const { result } = renderHook(() => useHistoryStore());

      await act(async () => {
        await result.current.loadMore();
      });

      expect(result.current.drafts).toHaveLength(20);

      await act(async () => {
        await result.current.loadMore();
      });

      expect(result.current.drafts).toHaveLength(30);
      expect(result.current.hasMore).toBe(false);
    });

    it('sets error state on failure', async () => {
      vi.mocked(DraftService.getCompletedDrafts).mockRejectedValue(
        new Error('Database error')
      );

      const { result } = renderHook(() => useHistoryStore());

      await act(async () => {
        await result.current.loadMore();
      });

      expect(result.current.error).toBe('Database error');
      expect(result.current.loading).toBe(false);
    });

    it('prevents duplicate loading calls', async () => {
      let resolvePromise: (value: any[]) => void;
      const pendingPromise = new Promise<any[]>(resolve => {
        resolvePromise = resolve;
      });

      vi.mocked(DraftService.getCompletedDrafts).mockReturnValue(pendingPromise);

      const { result } = renderHook(() => useHistoryStore());

      // Start first load
      act(() => {
        result.current.loadMore();
      });

      // Try to start second load while first is pending
      act(() => {
        result.current.loadMore();
      });

      // Still loading, should have prevented duplicate
      expect(result.current.loading).toBe(true);

      // Complete the first load
      await act(async () => {
        resolvePromise!([]);
      });

      // Should only call once due to loading check
      expect(DraftService.getCompletedDrafts).toHaveBeenCalledTimes(1);
    });
  });

  describe('refreshHistory', () => {
    it('clears existing drafts and reloads', async () => {
      const drafts = Array(20).fill(null).map((_, i) => ({
        ...mockDraft,
        id: i + 1
      }));

      vi.mocked(DraftService.getCompletedDrafts).mockResolvedValue(drafts);

      const { result } = renderHook(() => useHistoryStore());

      // First load some drafts
      await act(async () => {
        await result.current.loadMore();
      });

      expect(result.current.drafts).toHaveLength(20);

      // Now refresh
      vi.mocked(DraftService.getCompletedDrafts).mockClear().mockResolvedValue(drafts);

      await act(async () => {
        await result.current.refreshHistory();
      });

      expect(result.current.drafts).toHaveLength(20);
      expect(DraftService.getCompletedDrafts).toHaveBeenCalledWith({
        limit: 20,
        offset: 0
      });
    });
  });

  describe('selectDraft', () => {
    it('sets selected draft', () => {
      const { result } = renderHook(() => useHistoryStore());

      act(() => {
        result.current.selectDraft(mockDraft);
      });

      expect(result.current.selectedDraft).toEqual(mockDraft);
    });
  });

  describe('closeDetail', () => {
    it('clears selected draft', () => {
      const { result } = renderHook(() => useHistoryStore());

      act(() => {
        result.current.selectDraft(mockDraft);
      });

      expect(result.current.selectedDraft).toEqual(mockDraft);

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

      expect(result.current.selectedDraft).toBeNull();
    });
  });

  describe('clearError', () => {
    it('clears error state', async () => {
      vi.mocked(DraftService.getCompletedDrafts).mockRejectedValue(
        new Error('Test error')
      );

      const { result } = renderHook(() => useHistoryStore());

      await act(async () => {
        await result.current.loadMore();
      });

      expect(result.current.error).toBe('Test error');

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

      expect(result.current.error).toBeNull();
    });
  });

  describe('atomic selectors', () => {
    it('allows atomic selector access within component', () => {
      const { result } = renderHook(() => useHistoryStore());

      const drafts = result.current.drafts;
      const loading = result.current.loading;
      const hasMore = result.current.hasMore;

      expect(drafts).toEqual([]);
      expect(loading).toBe(false);
      expect(hasMore).toBe(true);
    });
  });
});
