import { afterEach, describe, expect, test, vi } from 'vitest';
import { render, screen } from '@testing-library/svelte';
import { flushSync } from 'svelte';
import { mockQuery } from '../../../test-utils/query';
import { emptyLikesMock } from '../../../test-utils/mocks/likes';
import type { AlbumDetail, TrackRef } from '$lib/api/types';
import { makeTrack } from '$test-utils/fixtures/track';

const state = vi.hoisted(() => ({ pageParams: { id: 'xyz' } as Record<string, string> }));

vi.mock('$app/state', () => ({
  page: {
    get params() { return state.pageParams; },
    get url() { return new URL('http://localhost/albums/' + state.pageParams.id); }
  }
}));

vi.mock('$lib/api/queries', () => ({
  qk: { album: (id: string) => ['album', id] },
  createAlbumQuery: vi.fn()
}));

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

import AlbumPage from './+page.svelte';
import { createAlbumQuery } from '$lib/api/queries';

function track(id: string, title: string, number: number, dur: number): TrackRef {
  return makeTrack({
    id, title,
    album_id: 'xyz', album_title: 'Kind of Blue',
    artist_id: 'md', artist_name: 'Miles Davis',
    track_number: number,
    duration_sec: dur,
    stream_url: `/api/tracks/${id}/stream`
  });
}

afterEach(() => {
  vi.clearAllMocks();
  state.pageParams = { id: 'xyz' };
});

describe('album detail page', () => {
  test('renders hero metadata + one TrackRow per track', () => {
    const detail: AlbumDetail = {
      id: 'xyz', title: 'Kind of Blue', sort_title: 'Kind of Blue',
      artist_id: 'md', artist_name: 'Miles Davis',
      year: 1959, track_count: 2, duration_sec: 544 + 565,
      cover_url: '/api/albums/xyz/cover',
      cover_art_source: null,
      tracks: [track('t1', 'So What', 1, 544), track('t2', 'Freddie Freeloader', 2, 565)],
      genres: ['Jazz']
    };
    (createAlbumQuery as ReturnType<typeof vi.fn>).mockReturnValue(mockQuery({ data: detail }));
    render(AlbumPage);

    expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('Kind of Blue');
    // Two links point at /artists/md: the back chevron and the inline hero link.
    // getAllByRole returns both; both should target the artist page.
    const artistLinks = screen.getAllByRole('link', { name: /Miles Davis/ });
    expect(artistLinks.length).toBeGreaterThanOrEqual(1);
    artistLinks.forEach((l) => expect(l).toHaveAttribute('href', '/artists/md'));
    expect(screen.getByText(/1959/)).toBeInTheDocument();
    expect(screen.getByText(/2 tracks/i)).toBeInTheDocument();

    expect(screen.getByText('So What')).toBeInTheDocument();
    expect(screen.getByText('Freddie Freeloader')).toBeInTheDocument();

    const img = screen.getByRole('img') as HTMLImageElement;
    expect(img.src).toContain('/api/albums/xyz/cover');
  });

  test('back link points to /artists/:artistId with the artist name', () => {
    const detail: AlbumDetail = {
      id: 'xyz', title: 'T', sort_title: 'T',
      artist_id: 'md', artist_name: 'Miles Davis',
      track_count: 0, duration_sec: 0,
      cover_url: '/api/albums/xyz/cover',
      cover_art_source: null,
      tracks: [],
      genres: []
    };
    (createAlbumQuery as ReturnType<typeof vi.fn>).mockReturnValue(mockQuery({ data: detail }));
    render(AlbumPage);
    // Back chevron specifically (disambiguated from the inline artist link by
    // the leading arrow character).
    const back = screen.getByRole('link', { name: /^← Miles Davis/ });
    expect(back).toHaveAttribute('href', '/artists/md');
  });

  test('404 renders non-retryable "Album not found" with back link', () => {
    (createAlbumQuery as ReturnType<typeof vi.fn>).mockReturnValue(mockQuery({
      isError: true,
      error: { code: 'not_found', message: 'nope', status: 404 }
    }));
    render(AlbumPage);
    expect(screen.getByText(/album not found/i)).toBeInTheDocument();
    expect(screen.queryByRole('button', { name: /try again/i })).not.toBeInTheDocument();
  });

  test('non-404 error renders the retry banner', () => {
    (createAlbumQuery as ReturnType<typeof vi.fn>).mockReturnValue(mockQuery({
      isError: true,
      error: { code: 'server_error', message: 'boom', status: 500 }
    }));
    render(AlbumPage);
    expect(screen.getByRole('alert')).toHaveTextContent('boom');
    expect(screen.getByRole('button', { name: /try again/i })).toBeInTheDocument();
  });

  test('pending (after delay) renders the album skeleton', () => {
    (createAlbumQuery as ReturnType<typeof vi.fn>).mockReturnValue(mockQuery({ isPending: true }));
    vi.useFakeTimers();
    try {
      render(AlbumPage);
      vi.advanceTimersByTime(200);
      flushSync();
      expect(screen.getByTestId('skeleton-album')).toBeInTheDocument();
    } finally {
      vi.useRealTimers();
    }
  });

  // Quick-jumps into the browse axes (#367). The genre href must be encoded:
  // a slash-bearing tag is why browse selection lives in the query string.
  test('year and genre are quick-jump links into the browse axes', () => {
    const detail: AlbumDetail = {
      id: 'xyz', title: 'Kind of Blue', sort_title: 'Kind of Blue',
      artist_id: 'md', artist_name: 'Miles Davis',
      year: 1959, track_count: 0, duration_sec: 0,
      cover_url: '/api/albums/xyz/cover',
      cover_art_source: null,
      tracks: [],
      genres: ['Jazz', 'Rock/Pop']
    };
    (createAlbumQuery as ReturnType<typeof vi.fn>).mockReturnValue(mockQuery({ data: detail }));
    render(AlbumPage);

    expect(screen.getByRole('link', { name: '1959' })).toHaveAttribute(
      'href',
      '/library/years?y=1959'
    );
    expect(screen.getByRole('link', { name: 'Jazz' })).toHaveAttribute(
      'href',
      '/library/genres?g=Jazz'
    );
    expect(screen.getByRole('link', { name: 'Rock/Pop' })).toHaveAttribute(
      'href',
      '/library/genres?g=Rock%2FPop'
    );
  });

  test('no genre chips when the album carries no genre tags', () => {
    const detail: AlbumDetail = {
      id: 'xyz', title: 'Untagged', sort_title: 'Untagged',
      artist_id: 'md', artist_name: 'Miles Davis',
      track_count: 0, duration_sec: 0,
      cover_url: '/api/albums/xyz/cover',
      cover_art_source: null,
      tracks: [],
      genres: []
    };
    (createAlbumQuery as ReturnType<typeof vi.fn>).mockReturnValue(mockQuery({ data: detail }));
    render(AlbumPage);

    expect(screen.queryByRole('link', { name: /library\/genres/ })).toBeNull();
    // No year on this fixture either, so no year jump.
    expect(screen.queryByRole('link', { name: /^\d{4}$/ })).toBeNull();
  });
});
