import { cleanup, fireEvent, render, screen, waitFor } from '@testing-library/react';
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { AddToPlaylistDialog } from './AddToPlaylistDialog';
import type { LibraryBrowseItem } from '../../api';

function jsonResponse(body: unknown, status = 200): Response {
  return new Response(JSON.stringify(body), { headers: { 'Content-Type': 'application/json' }, status });
}

const groups = [
  { id: 1, isSystem: false, name: 'Idents', playlistCount: 2 },
  { id: 2, isSystem: false, name: 'Bumpers', playlistCount: 1 }
];

const playlistsByGroup: Record<string, unknown[]> = {
  '1': [
    { id: 10, isSystem: false, name: 'Morning', playlistGroupId: 1 },
    { id: 11, isSystem: true, name: 'System Reserved', playlistGroupId: 1 }
  ],
  '2': [{ id: 20, isSystem: false, name: 'Evening', playlistGroupId: 2 }]
};

const showItem = { artwork: '', id: 5, mediaType: 'TelevisionShow', title: 'The Office' } as unknown as LibraryBrowseItem;

interface Recorded {
  url: string;
  method: string;
  body: unknown;
}

function mockApi() {
  const calls: Recorded[] = [];
  vi.spyOn(window, 'fetch').mockImplementation((input: RequestInfo | URL, init?: RequestInit) => {
    const url = input.toString();
    const method = (init?.method ?? 'GET').toUpperCase();
    const body = init?.body ? JSON.parse(String(init.body)) : undefined;
    calls.push({ body, method, url });

    if (url === '/api/v1/playlists/groups' && method === 'GET') {
      return Promise.resolve(jsonResponse(groups));
    }

    const playlistsMatch = /^\/api\/v1\/playlists\?playlistGroupId=(\d+)$/.exec(url);
    if (playlistsMatch && method === 'GET') {
      return Promise.resolve(jsonResponse(playlistsByGroup[playlistsMatch[1]] ?? []));
    }

    return Promise.resolve(new Response(null, { status: 204 }));
  });

  return { calls };
}

describe('AddToPlaylistDialog', () => {
  beforeEach(() => {
    window.localStorage.clear();
    vi.restoreAllMocks();
  });

  afterEach(() => {
    cleanup();
  });

  it('loads the first group and its playlists', async () => {
    mockApi();
    render(<AddToPlaylistDialog items={[showItem]} onClose={() => {}} open />);

    await waitFor(() => expect(screen.getByRole('option', { name: 'Morning' })).toBeTruthy());
    expect(screen.getByRole('option', { name: 'Idents' })).toBeTruthy();
  });

  it('adds the items to the selected playlist', async () => {
    const onAdded = vi.fn();
    const onClose = vi.fn();
    const { calls } = mockApi();
    render(<AddToPlaylistDialog items={[showItem]} onAdded={onAdded} onClose={onClose} open />);

    await waitFor(() => expect(screen.getByRole('option', { name: 'Morning' })).toBeTruthy());
    fireEvent.click(screen.getByRole('button', { name: /Add to playlist/ }));

    await waitFor(() => expect(onClose).toHaveBeenCalled());
    const addCall = calls.find((call) => call.url === '/api/v1/playlists/10/items');
    expect(addCall?.method).toBe('POST');
    expect(addCall?.body).toMatchObject({ showIds: [5], movieIds: [] });
    expect(onAdded).toHaveBeenCalledWith('Morning');
  });

  it('filters out system playlists and groups', async () => {
    vi.spyOn(window, 'fetch').mockImplementation((input: RequestInfo | URL, init?: RequestInit) => {
      const url = input.toString();
      const method = (init?.method ?? 'GET').toUpperCase();
      if (url === '/api/v1/playlists/groups' && method === 'GET') {
        return Promise.resolve(
          jsonResponse([...groups, { id: 9, isSystem: true, name: 'System Group', playlistCount: 1 }])
        );
      }
      const playlistsMatch = /^\/api\/v1\/playlists\?playlistGroupId=(\d+)$/.exec(url);
      if (playlistsMatch && method === 'GET') {
        return Promise.resolve(jsonResponse(playlistsByGroup[playlistsMatch[1]] ?? []));
      }
      return Promise.resolve(new Response(null, { status: 204 }));
    });

    render(<AddToPlaylistDialog items={[showItem]} onClose={() => {}} open />);

    await waitFor(() => expect(screen.getByRole('option', { name: 'Morning' })).toBeTruthy());
    expect(screen.queryByRole('option', { name: 'System Reserved' })).toBeNull();
    expect(screen.queryByRole('option', { name: 'System Group' })).toBeNull();
  });

  it('reloads playlists when the group changes', async () => {
    mockApi();
    render(<AddToPlaylistDialog items={[showItem]} onClose={() => {}} open />);

    await waitFor(() => expect(screen.getByRole('option', { name: 'Morning' })).toBeTruthy());
    const [groupSelect] = screen.getAllByRole('combobox');
    fireEvent.change(groupSelect, { target: { value: '2' } });

    await waitFor(() => expect(screen.getByRole('option', { name: 'Evening' })).toBeTruthy());
  });
});
