import { describe, it, expect, vi, afterEach } from 'vitest';
import { screen, waitFor, fireEvent } from '@solidjs/testing-library';
import { renderWithProviders } from '../test-utils';
import type { Source, BulkImportResponse } from '~/types';
import { MOCK_SOURCES } from '../fixtures';

// Mock the sources API module
vi.mock('~/api/sources', () => ({
  sourcesApi: {
    list: vi.fn(),
    create: vi.fn(),
    remove: vi.fn(),
    bulkImport: vi.fn(),
    importCsv: vi.fn(),
    exportCsv: vi.fn(),
  },
}));

// Also mock syntheses since sources imports from it (fetchFile, triggerDownload)
vi.mock('~/api/syntheses', () => ({
  synthesesApi: {
    list: vi.fn(),
    remove: vi.fn(),
    generate: vi.fn(),
    progressUrl: vi.fn(),
  },
  fetchFile: vi.fn(),
  triggerDownload: vi.fn(),
}));

import { sourcesApi } from '~/api/sources';
import Sources from '~/pages/Sources';

const mockedList = vi.mocked(sourcesApi.list);
const mockedCreate = vi.mocked(sourcesApi.create);
const mockedRemove = vi.mocked(sourcesApi.remove);
const mockedBulkImport = vi.mocked(sourcesApi.bulkImport);
const mockedExportCsv = vi.mocked(sourcesApi.exportCsv);

// Sources page tests use specific titles that the UI renders, so we define
// page-specific sources based on the shared fixture shape.
const pageSources: Source[] = [
  { ...MOCK_SOURCES[0], id: 'src1', title: 'OpenAI Blog', url: 'https://openai.com/blog', created_at: '2026-03-01T10:00:00Z' },
  { ...MOCK_SOURCES[1], id: 'src2', title: 'Google AI Blog', url: 'https://ai.googleblog.com', created_at: '2026-03-02T10:00:00Z' },
];

afterEach(() => {
  vi.clearAllMocks();
});

describe('Sources Page', () => {
  it('should render source list from API data', async () => {
    mockedList.mockResolvedValue(pageSources);

    renderWithProviders(() => <Sources />);

    await waitFor(() => {
      expect(screen.getByText('OpenAI Blog')).toBeInTheDocument();
    });
    expect(screen.getByText('Google AI Blog')).toBeInTheDocument();
  });

  it('should show empty state when no sources', async () => {
    mockedList.mockResolvedValue([]);

    renderWithProviders(() => <Sources />);

    await waitFor(() => {
      expect(
        screen.getByText('Aucune source personnalisee pour le moment.'),
      ).toBeInTheDocument();
    });
  });

  it('should validate empty title on add form submit', async () => {
    mockedList.mockResolvedValue([]);

    renderWithProviders(() => <Sources />);

    await waitFor(() => {
      expect(screen.getByText('Ajouter')).toBeInTheDocument();
    });

    // Fill URL but not title
    const urlInput = screen.getByPlaceholderText('https://...');
    fireEvent.input(urlInput, { target: { value: 'https://example.com' } });

    const addButton = screen.getByText('Ajouter');
    fireEvent.click(addButton);

    await waitFor(() => {
      expect(screen.getByText('Le titre est requis.')).toBeInTheDocument();
    });
  });

  it('should validate empty URL on add form submit', async () => {
    mockedList.mockResolvedValue([]);

    renderWithProviders(() => <Sources />);

    await waitFor(() => {
      expect(screen.getByText('Ajouter')).toBeInTheDocument();
    });

    const titleInput = screen.getByPlaceholderText(
      'Nom de la source (ex: Blog de Yann LeCun)',
    );
    fireEvent.input(titleInput, { target: { value: 'My Blog' } });

    const addButton = screen.getByText('Ajouter');
    fireEvent.click(addButton);

    await waitFor(() => {
      expect(screen.getByText("L'URL est requise.")).toBeInTheDocument();
    });
  });

  it('should call API and refresh list when add form is submitted', async () => {
    const newSource: Source = {
      id: 'src-new',
      user_id: 'u1',
      title: 'New Blog',
      url: 'https://newblog.com',
      created_at: '2026-03-20T10:00:00Z',
    };
    mockedCreate.mockResolvedValue(newSource);
    // First call returns empty, second call after create returns the new source
    mockedList.mockResolvedValueOnce([]).mockResolvedValueOnce([newSource]);

    renderWithProviders(() => <Sources />);

    await waitFor(() => {
      expect(screen.getByText('Ajouter')).toBeInTheDocument();
    });

    const titleInput = screen.getByPlaceholderText(
      'Nom de la source (ex: Blog de Yann LeCun)',
    );
    const urlInput = screen.getByPlaceholderText('https://...');

    fireEvent.input(titleInput, { target: { value: 'New Blog' } });
    fireEvent.input(urlInput, { target: { value: 'https://newblog.com' } });

    const addButton = screen.getByText('Ajouter');
    fireEvent.click(addButton);

    await waitFor(() => {
      expect(mockedCreate).toHaveBeenCalledWith({
        title: 'New Blog',
        url: 'https://newblog.com',
      });
    });
  });

  it('should handle delete with confirmation flow', async () => {
    mockedRemove.mockResolvedValue(undefined);
    // First call returns all sources, second call after delete returns one
    mockedList
      .mockResolvedValueOnce(pageSources)
      .mockResolvedValueOnce([pageSources[1]]);

    renderWithProviders(() => <Sources />);

    await waitFor(() => {
      expect(screen.getByText('OpenAI Blog')).toBeInTheDocument();
    });

    // First click on delete for first source
    const deleteButtons = screen.getAllByTitle('Supprimer');
    fireEvent.click(deleteButtons[0]);

    await waitFor(() => {
      expect(screen.getByText('Confirmer ?')).toBeInTheDocument();
    });

    // Second click to confirm
    const confirmBtn = screen.getByText('Confirmer ?');
    fireEvent.click(confirmBtn);

    await waitFor(() => {
      expect(mockedRemove).toHaveBeenCalledWith('src1');
    });
  });

  it('should trigger bulk import API call', async () => {
    const bulkResponse: BulkImportResponse = {
      imported: 2,
      skipped: 0,
      errors: [],
    };
    mockedBulkImport.mockResolvedValue(bulkResponse);
    mockedList.mockResolvedValueOnce([]).mockResolvedValueOnce(pageSources);

    renderWithProviders(() => <Sources />);

    await waitFor(() => {
      expect(screen.getByText('Importer les sources')).toBeInTheDocument();
    });

    // The textarea has id="bulk-import"
    const textarea = document.getElementById('bulk-import') as HTMLTextAreaElement;
    expect(textarea).toBeTruthy();
    fireEvent.input(textarea, {
      target: {
        value: 'OpenAI Blog;https://openai.com/blog\nGoogle AI Blog;https://ai.googleblog.com',
      },
    });

    const importButton = screen.getByText('Importer les sources');
    fireEvent.click(importButton);

    await waitFor(() => {
      expect(mockedBulkImport).toHaveBeenCalledWith({
        sources: [
          { title: 'OpenAI Blog', url: 'https://openai.com/blog' },
          { title: 'Google AI Blog', url: 'https://ai.googleblog.com' },
        ],
      });
    });
  });

  it('should trigger CSV export download', async () => {
    mockedList.mockResolvedValue(pageSources);
    mockedExportCsv.mockResolvedValue(undefined);

    renderWithProviders(() => <Sources />);

    await waitFor(() => {
      expect(screen.getByText('Exporter en CSV')).toBeInTheDocument();
    });

    const exportButton = screen.getByText('Exporter en CSV');
    fireEvent.click(exportButton);

    await waitFor(() => {
      expect(mockedExportCsv).toHaveBeenCalled();
    });
  });
});
