import 'package:flutter/material.dart';
import 'package:flutter_lucide/flutter_lucide.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:go_router/go_router.dart';

import '../api/endpoints/likes.dart';
import '../likes/like_button.dart';
import '../models/album.dart';
import '../models/artist.dart';
import '../player/player_provider.dart';
import '../shared/widgets/server_image.dart';
import '../theme/theme_extension.dart';
import 'library_providers.dart';
import 'widgets/album_card.dart';

class ArtistDetailScreen extends ConsumerWidget {
  const ArtistDetailScreen({required this.id, this.seed, super.key});
  final String id;

  /// Optional artist reference from the caller. Lets the screen render
  /// the name + avatar immediately while albums load.
  final ArtistRef? seed;

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final fs = Theme.of(context).extension<FabledSwordTheme>()!;
    final artist = ref.watch(artistProvider(id));
    final albums = ref.watch(artistAlbumsProvider(id));

    // Resolve which artist info to render in the header. Live wins
    // when present and populated; seed fills the gap during the
    // first frame after navigation.
    final liveArtist = artist.value;
    final hasLiveName = liveArtist != null && liveArtist.name.isNotEmpty;
    final effective = hasLiveName ? liveArtist : (seed ?? liveArtist);

    return Scaffold(
      appBar: AppBar(),
      backgroundColor: fs.obsidian,
      body: artist.when(
        error: (e, _) => Center(child: Text('$e', style: TextStyle(color: fs.error))),
        // While loading: render header from seed if available so the
        // page isn't blank.
        loading: () => seed == null
            ? const Center(child: CircularProgressIndicator())
            : _artistBody(context, ref, seed!, albums, fs),
        data: (_) => _artistBody(context, ref, effective ?? seed!, albums, fs),
      ),
    );
  }

  Widget _artistBody(
    BuildContext context,
    WidgetRef ref,
    ArtistRef a,
    AsyncValue<List<AlbumRef>> albums,
    FabledSwordTheme fs,
  ) =>
      ListView(children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(children: [
              ClipOval(
                child: SizedBox(
                  width: 96,
                  height: 96,
                  // Server derives artist cover from a representative
                  // album. Drift cache doesn't persist that pointer, so
                  // mirror the trick client-side: reuse the first album
                  // returned by artistAlbumsProvider. Falls back to
                  // slate while albums are loading or empty.
                  child: _ArtistAvatar(
                    serverCoverUrl: a.coverUrl,
                    albums: albums,
                    fs: fs,
                  ),
                ),
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Text(
                  a.name,
                  style: TextStyle(
                    color: fs.parchment,
                    fontFamily: fs.display.fontFamily,
                    fontSize: 24,
                  ),
                ),
              ),
              Container(
                width: 48, height: 48,
                decoration: BoxDecoration(color: fs.accent, shape: BoxShape.circle),
                child: IconButton(
                  icon: Icon(LucideIcons.play, color: fs.parchment),
                  onPressed: () async {
                    try {
                      final tracks =
                          await ref.read(artistTracksProvider(id).future);
                      if (tracks.isEmpty) {
                        if (context.mounted) {
                          ScaffoldMessenger.of(context).showSnackBar(
                            const SnackBar(
                                content: Text(
                                    'No tracks found for this artist yet.')),
                          );
                        }
                        return;
                      }
                      final shuffled = [...tracks]..shuffle();
                      await ref
                          .read(playerActionsProvider)
                          .playTracks(shuffled);
                    } catch (e) {
                      debugPrint('artist_detail: play failed: $e');
                      if (context.mounted) {
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(content: Text("Couldn't start playback: $e")),
                        );
                      }
                    }
                  },
                ),
              ),
              LikeButton(kind: LikeKind.artist, id: a.id, size: 28),
            ]),
          ),
          const Padding(
            padding: EdgeInsets.fromLTRB(16, 16, 16, 8),
            child: Text('Albums', style: TextStyle(fontSize: 16)),
          ),
          albums.when(
            error: (e, _) => Center(child: Text('$e', style: TextStyle(color: fs.error))),
            loading: () => const Padding(padding: EdgeInsets.all(16), child: Center(child: CircularProgressIndicator())),
            data: (list) {
              return LayoutBuilder(builder: (ctx, constraints) {
              const cols = 3;
              const sidePad = 8.0;
              const gap = 8.0;
              final cellW =
                  (constraints.maxWidth - sidePad * 2 - gap * (cols - 1)) /
                      cols;
              // Card content: cover (cellW - 16) + 8 + title (≤2 lines
              // ≈ 36) + slack. Artist line is suppressed in this
              // grid (showArtist: false) since the page header already
              // names the artist. Slack is generous on purpose — line-
              // height variations would otherwise overflow by 1px.
              final cellH = (cellW - 16) + 8 + 36 + 8;
              return GridView.builder(
                shrinkWrap: true,
                physics: const NeverScrollableScrollPhysics(),
                padding: const EdgeInsets.fromLTRB(sidePad, 0, sidePad, 16),
                gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
                  crossAxisCount: cols,
                  mainAxisExtent: cellH,
                  mainAxisSpacing: gap,
                  crossAxisSpacing: gap,
                ),
                itemCount: list.length,
                itemBuilder: (_, i) {
                  final AlbumRef album = list[i];
                  return AlbumCard(
                    album: album,
                    width: cellW,
                    titleMaxLines: 2,
                    showArtist: false,
                    onTap: () =>
                        context.push('/albums/${album.id}', extra: album),
                  );
                },
              );
            });
            },
          ),
        ]);
}

/// Renders the artist's avatar. Server-emitted coverUrl wins when
/// non-empty; otherwise we mirror the server's "use the first album's
/// cover" rule client-side via the loaded album list.
class _ArtistAvatar extends StatelessWidget {
  const _ArtistAvatar({
    required this.serverCoverUrl,
    required this.albums,
    required this.fs,
  });
  final String serverCoverUrl;
  final AsyncValue<List<AlbumRef>> albums;
  final FabledSwordTheme fs;

  @override
  Widget build(BuildContext context) {
    if (serverCoverUrl.isNotEmpty) {
      return ServerImage(
        url: serverCoverUrl,
        fit: BoxFit.cover,
        fallback: Container(color: fs.slate),
      );
    }
    final firstId = albums.value?.isNotEmpty == true ? albums.value!.first.id : null;
    if (firstId == null) {
      return Container(color: fs.slate);
    }
    return ServerImage(
      url: '/api/albums/$firstId/cover',
      fit: BoxFit.cover,
      fallback: Container(color: fs.slate),
    );
  }
}
