import 'package:cached_network_image/cached_network_image.dart';
import 'package:dio/dio.dart';
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/discover.dart';
import '../api/errors.dart';
import '../cache/mutation_queue.dart';
import '../library/library_providers.dart' show dioProvider;
import '../models/artist_suggestion.dart';
import '../models/lidarr.dart';
import '../shared/widgets/main_app_bar_actions.dart';
import '../theme/theme_extension.dart';

final _discoverApiProvider = FutureProvider<DiscoverApi>((ref) async {
  return DiscoverApi(await ref.watch(dioProvider.future));
});

class DiscoverScreen extends ConsumerStatefulWidget {
  const DiscoverScreen({super.key});

  @override
  ConsumerState<DiscoverScreen> createState() => _DiscoverScreenState();
}

class _DiscoverScreenState extends ConsumerState<DiscoverScreen> {
  final _ctrl = TextEditingController();
  LidarrRequestKind _kind = LidarrRequestKind.artist;
  Future<List<LidarrSearchResult>>? _resultsFuture;
  // Default (empty-search) surface: LB-derived out-of-library artist
  // suggestions, mirroring web's SuggestionFeed.
  Future<List<ArtistSuggestion>>? _suggestionsFuture;
  final _requested = <String>{};

  @override
  void initState() {
    super.initState();
    _loadSuggestions();
    // Clearing the box returns to suggestions (web swaps live too).
    _ctrl.addListener(() {
      if (_ctrl.text.trim().isEmpty && _resultsFuture != null) {
        setState(() => _resultsFuture = null);
      }
    });
  }

  @override
  void dispose() {
    _ctrl.dispose();
    super.dispose();
  }

  // Assigns the future only; callers trigger the rebuild (initState
  // runs before first build, so setState here would be a no-op/warn).
  void _loadSuggestions() {
    _suggestionsFuture = ref
        .read(_discoverApiProvider.future)
        .then((api) => api.listSuggestions());
  }

  Future<void> _requestSuggestion(ArtistSuggestion s) async {
    final fs = Theme.of(context).extension<FabledSwordTheme>()!;
    final args = {
      'kind': LidarrRequestKind.artist.wire,
      'artistMbid': s.mbid,
      'artistName': s.name,
      'albumMbid': null,
      'albumTitle': null,
    };
    try {
      final api = await ref.read(_discoverApiProvider.future);
      await api.createRequest(
        kind: LidarrRequestKind.artist,
        artistMbid: s.mbid,
        artistName: s.name,
      );
      if (mounted) {
        // Reassign the future first; the setState below rebuilds and
        // the FutureBuilder picks up the fresh fetch (server now
        // filters this candidate out). _requested hides it meanwhile.
        _loadSuggestions();
        setState(() => _requested.add(s.mbid));
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: Text('Requested: ${s.name}'),
          backgroundColor: fs.iron,
        ));
      }
    } on DioException catch (_) {
      await ref
          .read(mutationQueueProvider)
          .enqueue(MutationKinds.requestCreate, args);
      if (mounted) {
        setState(() => _requested.add(s.mbid));
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: Text('Request queued: ${s.name}'),
          backgroundColor: fs.iron,
        ));
      }
    }
  }

  void _runSearch() {
    final q = _ctrl.text.trim();
    if (q.isEmpty) {
      setState(() => _resultsFuture = null);
      return;
    }
    setState(() {
      _resultsFuture = ref
          .read(_discoverApiProvider.future)
          .then((api) => api.search(q, _kind));
    });
  }

  Future<void> _request(LidarrSearchResult row) async {
    final fs = Theme.of(context).extension<FabledSwordTheme>()!;
    final args = {
      'kind': _kind.wire,
      'artistMbid':
          _kind == LidarrRequestKind.artist ? row.mbid : row.artistMbid,
      'artistName':
          _kind == LidarrRequestKind.artist ? row.name : row.secondaryText,
      'albumMbid': _kind == LidarrRequestKind.album ? row.mbid : null,
      'albumTitle': _kind == LidarrRequestKind.album ? row.name : null,
    };
    try {
      final api = await ref.read(_discoverApiProvider.future);
      await api.createRequest(
        kind: _kind,
        artistMbid: args['artistMbid'] as String,
        artistName: args['artistName'] as String,
        albumMbid: args['albumMbid'],
        albumTitle: args['albumTitle'],
      );
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: Text('Requested: ${row.name}'),
          backgroundColor: fs.iron,
        ));
        // Re-run search to refresh the `requested` flag on the row.
        _runSearch();
      }
    } on DioException catch (_) {
      // Queue for replay so the user's request persists across
      // network loss. We don't have a drift table for in-flight
      // requests yet, so the row won't show on the Requests screen
      // until replay succeeds — accept that for v1.
      await ref
          .read(mutationQueueProvider)
          .enqueue(MutationKinds.requestCreate, args);
      if (mounted) {
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(
          content: Text('Request queued: ${row.name}'),
          backgroundColor: fs.iron,
        ));
      }
    }
  }

  @override
  Widget build(BuildContext context) {
    final fs = Theme.of(context).extension<FabledSwordTheme>()!;
    return Scaffold(
      backgroundColor: fs.obsidian,
      appBar: AppBar(
        backgroundColor: fs.obsidian,
        elevation: 0,
        leading: IconButton(
          icon: Icon(LucideIcons.arrow_left, color: fs.parchment),
          onPressed: () => context.pop(),
        ),
        title: Text('Discover', style: TextStyle(color: fs.parchment)),
        actions: const [MainAppBarActions(currentRoute: '/discover')],
      ),
      body: Column(children: [
        Padding(
          padding: const EdgeInsets.fromLTRB(16, 8, 16, 4),
          child: Row(children: [
            Expanded(
              child: TextField(
                controller: _ctrl,
                style: TextStyle(color: fs.parchment),
                cursorColor: fs.accent,
                onSubmitted: (_) => _runSearch(),
                decoration: InputDecoration(
                  hintText: 'Search Lidarr for new music',
                  hintStyle: TextStyle(color: fs.ash),
                  enabledBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: fs.iron),
                  ),
                  focusedBorder: OutlineInputBorder(
                    borderSide: BorderSide(color: fs.accent),
                  ),
                  suffixIcon: IconButton(
                    icon: Icon(LucideIcons.search, color: fs.ash),
                    onPressed: _runSearch,
                  ),
                ),
              ),
            ),
          ]),
        ),
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),
          child: SegmentedButton<LidarrRequestKind>(
            segments: const [
              ButtonSegment(value: LidarrRequestKind.artist, label: Text('Artists')),
              ButtonSegment(value: LidarrRequestKind.album, label: Text('Albums')),
            ],
            selected: {_kind},
            onSelectionChanged: (s) {
              setState(() {
                _kind = s.first;
                if (_ctrl.text.trim().isNotEmpty) _runSearch();
              });
            },
          ),
        ),
        Expanded(
          child: _resultsFuture == null
              ? _buildSuggestions(fs)
              : FutureBuilder<List<LidarrSearchResult>>(
                  future: _resultsFuture,
                  builder: (ctx, snap) {
                    if (snap.connectionState != ConnectionState.done) {
                      return const Center(child: CircularProgressIndicator());
                    }
                    if (snap.hasError) {
                      final err = snap.error;
                      final code = err is DioException
                          ? ApiError.fromDio(err).code
                          : 'unknown';
                      return Center(
                        child: Text('Search failed: $code',
                            style: TextStyle(color: fs.error)),
                      );
                    }
                    final rows = snap.data ?? const [];
                    if (rows.isEmpty) {
                      return Center(
                        child: Text('Nothing to add for that search yet.',
                            style: TextStyle(color: fs.ash),
                            textAlign: TextAlign.center),
                      );
                    }
                    return ListView.separated(
                      itemCount: rows.length,
                      separatorBuilder: (_, __) =>
                          Divider(height: 1, color: fs.iron),
                      itemBuilder: (ctx, i) => _ResultTile(
                        row: rows[i],
                        onRequest: () => _request(rows[i]),
                      ),
                    );
                  },
                ),
        ),
      ]),
    );
  }

  Widget _buildSuggestions(FabledSwordTheme fs) {
    return FutureBuilder<List<ArtistSuggestion>>(
      future: _suggestionsFuture,
      builder: (ctx, snap) {
        if (snap.connectionState != ConnectionState.done) {
          return const Center(child: CircularProgressIndicator());
        }
        final items = (snap.data ?? const <ArtistSuggestion>[])
            .where((s) => !_requested.contains(s.mbid))
            .toList(growable: false);
        return ListView(
          padding: const EdgeInsets.only(bottom: 16),
          children: [
            Padding(
              padding: const EdgeInsets.fromLTRB(16, 12, 16, 4),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Suggested for you',
                      style: TextStyle(
                          color: fs.parchment,
                          fontSize: 20,
                          fontWeight: FontWeight.w500)),
                  const SizedBox(height: 2),
                  Text(
                    "Out-of-library artists drawn from what you've liked and played.",
                    style: TextStyle(color: fs.ash, fontSize: 13),
                  ),
                ],
              ),
            ),
            if (snap.hasError)
              Padding(
                padding: const EdgeInsets.all(16),
                child: Text("Couldn't load suggestions.",
                    style: TextStyle(color: fs.ash)),
              )
            else if (items.isEmpty)
              Padding(
                padding: const EdgeInsets.all(16),
                child: Text(
                  'Listen to something or like an artist to start getting suggestions.',
                  style: TextStyle(color: fs.ash),
                ),
              )
            else
              ...items.map((s) => _SuggestionTile(
                    s: s,
                    onRequest: () => _requestSuggestion(s),
                  )),
          ],
        );
      },
    );
  }
}

class _ResultTile extends StatelessWidget {
  const _ResultTile({required this.row, required this.onRequest});
  final LidarrSearchResult row;
  final VoidCallback onRequest;

  @override
  Widget build(BuildContext context) {
    final fs = Theme.of(context).extension<FabledSwordTheme>()!;
    final disabled = row.inLibrary || row.requested;
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
      child: Row(children: [
        ClipRRect(
          borderRadius: BorderRadius.circular(6),
          child: Container(
            width: 56,
            height: 56,
            color: fs.slate,
            child: row.imageUrl.isEmpty
                ? Icon(LucideIcons.disc_3, color: fs.ash)
                : CachedNetworkImage(
                    imageUrl: row.imageUrl,
                    fit: BoxFit.cover,
                    fadeInDuration: const Duration(milliseconds: 120),
                    fadeOutDuration: Duration.zero,
                    errorWidget: (_, __, ___) =>
                        Icon(LucideIcons.disc_3, color: fs.ash),
                  ),
          ),
        ),
        const SizedBox(width: 12),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(row.name,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(color: fs.parchment, fontSize: 14)),
              if (row.secondaryText.isNotEmpty)
                Text(row.secondaryText,
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    style: TextStyle(color: fs.ash, fontSize: 12)),
            ],
          ),
        ),
        const SizedBox(width: 8),
        if (row.inLibrary)
          _Pill(label: 'In library', color: fs.ash)
        else if (row.requested)
          _Pill(label: 'Requested', color: fs.ash)
        else
          FilledButton(
            onPressed: disabled ? null : onRequest,
            style: FilledButton.styleFrom(
              backgroundColor: fs.accent,
              foregroundColor: fs.parchment,
            ),
            child: const Text('Request'),
          ),
      ]),
    );
  }
}

class _Pill extends StatelessWidget {
  const _Pill({required this.label, required this.color});
  final String label;
  final Color color;
  @override
  Widget build(BuildContext context) {
    final fs = Theme.of(context).extension<FabledSwordTheme>()!;
    return Container(
      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
      decoration: BoxDecoration(
        color: fs.iron,
        borderRadius: BorderRadius.circular(4),
      ),
      child: Text(label, style: TextStyle(color: color, fontSize: 11)),
    );
  }
}

class _SuggestionTile extends StatelessWidget {
  const _SuggestionTile({required this.s, required this.onRequest});
  final ArtistSuggestion s;
  final VoidCallback onRequest;

  @override
  Widget build(BuildContext context) {
    final fs = Theme.of(context).extension<FabledSwordTheme>()!;
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
      child: Row(children: [
        ClipRRect(
          borderRadius: BorderRadius.circular(6),
          child: Container(
            width: 56,
            height: 56,
            color: fs.slate,
            child: s.imageUrl.isEmpty
                ? Icon(LucideIcons.user, color: fs.ash)
                : CachedNetworkImage(
                    imageUrl: s.imageUrl,
                    fit: BoxFit.cover,
                    fadeInDuration: const Duration(milliseconds: 120),
                    fadeOutDuration: Duration.zero,
                    errorWidget: (_, __, ___) =>
                        Icon(LucideIcons.user, color: fs.ash),
                  ),
          ),
        ),
        const SizedBox(width: 12),
        Expanded(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Text(s.name,
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                  style: TextStyle(color: fs.parchment, fontSize: 14)),
              if (s.attributionText.isNotEmpty)
                Text(s.attributionText,
                    maxLines: 2,
                    overflow: TextOverflow.ellipsis,
                    style: TextStyle(color: fs.ash, fontSize: 12)),
            ],
          ),
        ),
        const SizedBox(width: 8),
        FilledButton(
          onPressed: onRequest,
          style: FilledButton.styleFrom(
            backgroundColor: fs.accent,
            foregroundColor: fs.parchment,
          ),
          child: const Text('Request'),
        ),
      ]),
    );
  }
}
