import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen, fireEvent } from '@testing-library/svelte';
import { emptyLikesMock } from '../../test-utils/mocks/likes';
import { emptyQuarantineMock } from '../../test-utils/mocks/quarantine';
import type { PlaylistTrack } from '$lib/api/types';

// LikeButton + TrackMenu transitively pull in TanStack Query, the auth
// store, likes/quarantine APIs, the player store, and SvelteKit
// navigation. Mock all of them at module level so the component can be
// rendered in isolation.

vi.mock('$lib/auth/store.svelte', () => ({
  user: { value: { id: 'u1', username: 'me', is_admin: false } }
}));

vi.mock('$app/navigation', () => ({
  goto: vi.fn()
}));

vi.mock('$lib/api/likes', () => emptyLikesMock());

vi.mock('$lib/api/quarantine', () => emptyQuarantineMock());

vi.mock('$lib/player/store.svelte', () => ({
  playNext: vi.fn(),
  enqueueTrack: vi.fn(),
  playQueue: vi.fn(),
  playRadio: vi.fn(),
  player: { current: undefined }
}));

import PlaylistTrackRow from './PlaylistTrackRow.svelte';

const live: PlaylistTrack = {
  position: 2,
  track_id: 't-1',
  album_id: 'a-1',
  artist_id: 'ar-1',
  title: 'Roygbiv',
  artist_name: 'Boards of Canada',
  album_title: 'MHTRTC',
  duration_sec: 137,
  stream_url: '/api/tracks/t-1/stream',
  added_at: ''
};

const removed: PlaylistTrack = {
  ...live,
  track_id: null,
  album_id: null,
  artist_id: null,
  stream_url: null
};

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

describe('PlaylistTrackRow', () => {
  test('renders title, artist, mm:ss duration', () => {
    render(PlaylistTrackRow, {
      props: { row: live, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn() }
    });
    expect(screen.getByText('Roygbiv')).toBeInTheDocument();
    expect(screen.getByText('2:17')).toBeInTheDocument();
  });

  test('shows reorder handle and remove button for owner', () => {
    render(PlaylistTrackRow, {
      props: { row: live, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn() }
    });
    expect(screen.getByLabelText(/reorder track/i)).toBeInTheDocument();
    expect(
      screen.getByLabelText(/Remove Roygbiv from playlist/i)
    ).toBeInTheDocument();
  });

  test('hides reorder handle and remove button for non-owner', () => {
    render(PlaylistTrackRow, {
      props: { row: live, isOwner: false, onRemove: vi.fn(), onPlay: vi.fn() }
    });
    expect(screen.queryByLabelText(/reorder track/i)).not.toBeInTheDocument();
    expect(
      screen.queryByLabelText(/Remove Roygbiv from playlist/i)
    ).not.toBeInTheDocument();
  });

  test('ArrowDown on the reorder handle calls onMove(pos, pos+1)', async () => {
    const onMove = vi.fn();
    render(PlaylistTrackRow, {
      props: { row: live, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn(), onMove }
    });
    const handle = screen.getByLabelText(/reorder track/i);
    await fireEvent.keyDown(handle, { key: 'ArrowDown' });
    expect(onMove).toHaveBeenCalledWith(2, 3);
  });

  test('ArrowUp on the reorder handle calls onMove(pos, pos-1)', async () => {
    const onMove = vi.fn();
    render(PlaylistTrackRow, {
      props: { row: live, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn(), onMove }
    });
    const handle = screen.getByLabelText(/reorder track/i);
    await fireEvent.keyDown(handle, { key: 'ArrowUp' });
    expect(onMove).toHaveBeenCalledWith(2, 1);
  });

  test('greyed-out + strikethrough when track_id is null', () => {
    render(PlaylistTrackRow, {
      props: { row: removed, isOwner: true, onRemove: vi.fn(), onPlay: vi.fn() }
    });
    const titleEl = screen.getByText('Roygbiv');
    expect(titleEl.className).toContain('line-through');
  });

  test('clicking the title fires onPlay with the position', async () => {
    const onPlay = vi.fn();
    render(PlaylistTrackRow, {
      props: { row: live, isOwner: true, onRemove: vi.fn(), onPlay }
    });
    await fireEvent.click(screen.getByText('Roygbiv'));
    expect(onPlay).toHaveBeenCalledWith(2);
  });
});
