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

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

interface RemoteState {
  isAuthorized: boolean;
  isLocked: boolean;
  servers: Array<{ id: number; name: string; address: string }>;
}

function installFetch(family: string, stateRef: { current: RemoteState }) {
  return vi.spyOn(window, 'fetch').mockImplementation((input: RequestInfo | URL, init?: RequestInit) => {
    const url = typeof input === 'string' ? input : input instanceof URL ? input.toString() : input.url;
    const method = (init?.method ?? 'GET').toUpperCase();

    if (url.endsWith(`/api/v1/media-sources/${family}/disconnect`) && method === 'POST') {
      return Promise.resolve(new Response(null, { status: 204 }));
    }
    if (url.includes('/refresh-libraries') && method === 'POST') {
      return Promise.resolve(new Response(null, { status: 204 }));
    }
    if (url.endsWith(`/api/v1/media-sources/${family}`) && method === 'GET') {
      return Promise.resolve(json(stateRef.current));
    }
    return Promise.resolve(json({}, 404));
  });
}

describe('RemoteSourceScreen', () => {
  beforeEach(() => {
    vi.restoreAllMocks();
    window.history.replaceState(null, '', '/app/libraries/jellyfin');
  });

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

  it('shows a Connect action when no server is connected', async () => {
    const stateRef = { current: { isAuthorized: false, isLocked: false, servers: [] } as RemoteState };
    installFetch('jellyfin', stateRef);

    render(<RemoteSourceScreen family="jellyfin" />);

    expect(await screen.findByRole('button', { name: 'Connect' })).toBeTruthy();
    expect(screen.getByText(/Connect a Jellyfin server to sync/i)).toBeTruthy();
  });

  it('lists servers with Refresh disabled while the family lock is held', async () => {
    const stateRef = { current: { isAuthorized: true, isLocked: true, servers: [{ id: 3, name: 'Media Box', address: 'http://jf:8096' }] } as RemoteState };
    installFetch('jellyfin', stateRef);

    render(<RemoteSourceScreen family="jellyfin" />);

    await screen.findByRole('heading', { name: 'Servers' });
    expect(screen.getByText('Media Box')).toBeTruthy();
    const refresh = screen.getByRole('button', { name: /Refresh Media Box/i }) as HTMLButtonElement;
    expect(refresh.disabled).toBe(true);
  });

  it('disconnect shows the content-removal warning and posts on confirm', async () => {
    const stateRef = { current: { isAuthorized: true, isLocked: false, servers: [{ id: 3, name: 'Media Box', address: 'http://jf:8096' }] } as RemoteState };
    const fetchSpy = installFetch('jellyfin', stateRef);

    render(<RemoteSourceScreen family="jellyfin" />);
    await screen.findByRole('heading', { name: 'Servers' });

    fireEvent.click(screen.getByRole('button', { name: 'Disconnect' }));
    expect(await screen.findByText(/removes all synced Jellyfin libraries/i)).toBeTruthy();

    // The header button and the dialog confirm share the label; the dialog's is the last one.
    const disconnects = screen.getAllByRole('button', { name: 'Disconnect' });
    fireEvent.click(disconnects[disconnects.length - 1]);
    await vi.waitFor(() =>
      expect(fetchSpy.mock.calls.some(([input, init]) => String(input).endsWith('/disconnect') && (init?.method ?? '').toUpperCase() === 'POST')).toBe(true)
    );
  });

  it('uses the family label for Emby', async () => {
    const stateRef = { current: { isAuthorized: false, isLocked: false, servers: [] } as RemoteState };
    installFetch('emby', stateRef);

    render(<RemoteSourceScreen family="emby" />);

    expect(await screen.findByRole('heading', { name: 'Emby' })).toBeTruthy();
    expect(screen.getByText(/Connect a Emby server to sync/i)).toBeTruthy();
  });
});
