import 'package:auto_route/auto_route.dart';
import 'package:fast_immutable_collections/fast_immutable_collections.dart';
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:riverpod_annotation/riverpod_annotation.dart';

import '../../database/database.dart';
import '../../models/music.dart';
import '../../models/query.dart';
import '../../models/support.dart';
import '../../services/audio_service.dart';
import '../../state/music.dart';
import '../../state/settings.dart';
import '../app_router.dart';
import '../items.dart';
import 'songs_page.dart';

part 'search_page.g.dart';

@riverpod
class SearchQuery extends _$SearchQuery {
  @override
  String? build() {
    return null;
  }

  void setQuery(String query) {
    state = query;
  }
}

@riverpod
FutureOr<SearchResults> searchResult(SearchResultRef ref) async {
  final query = ref.watch(searchQueryProvider);
  final db = ref.watch(databaseProvider);
  final sourceId = ref.watch(sourceIdProvider);

  final ftsQuery = '(source_id : $sourceId) AND (- source_id : "$query"*)';

  final songRowIds = await db.searchSongs(ftsQuery, 5, 0).get();
  final songs = await db.songsInRowIds(songRowIds).get();
  final albumRowIds = await db.searchAlbums(ftsQuery, 5, 0).get();
  final albums = await db.albumsInRowIds(albumRowIds).get();
  final artistRowIds = await db.searchArtists(ftsQuery, 5, 0).get();
  final artists = await db.artistsInRowIds(artistRowIds).get();

  return SearchResults(
    songs: songs.lock,
    albums: albums.lock,
    artists: artists.lock,
  );
}

class SearchPage extends HookConsumerWidget {
  const SearchPage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final results = ref.watch(searchResultProvider).valueOrNull;

    return KeyboardDismissOnTap(
      dismissOnCapturedTaps: true,
      child: Scaffold(
        body: SafeArea(
          child: CustomScrollView(
            reverse: true,
            slivers: [
              const SliverToBoxAdapter(child: _SearchBar()),
              if (results != null && results.songs.isNotEmpty)
                _SongsSection(songs: results.songs),
              if (results != null && results.albums.isNotEmpty)
                _AlbumsSection(albums: results.albums),
              if (results != null && results.artists.isNotEmpty)
                _ArtistsSection(artists: results.artists),
              if (results != null)
                const SliverPadding(padding: EdgeInsets.only(top: 96))
            ],
          ),
        ),
      ),
    );
  }
}

class _SearchBar extends HookConsumerWidget {
  const _SearchBar();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final controller = useTextEditingController(text: '');

    final theme = Theme.of(context);
    final l = AppLocalizations.of(context);

    return Container(
      color: ElevationOverlay.applySurfaceTint(
        theme.colorScheme.surface,
        theme.colorScheme.surfaceTint,
        1,
      ),
      child: Padding(
        padding: const EdgeInsets.only(
          right: 24,
          left: 24,
          bottom: 24,
          top: 8,
        ),
        child: IgnoreKeyboardDismiss(
          child: TextFormField(
            controller: controller,
            decoration: InputDecoration(
              hintText: l.searchInputPlaceholder,
            ),
            onChanged: (value) {
              ref.read(searchQueryProvider.notifier).setQuery(value);
            },
          ),
        ),
      ),
    );
  }
}

class _SectionHeader extends HookConsumerWidget {
  final String title;

  const _SectionHeader({required this.title});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final theme = Theme.of(context).textTheme;

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
      child: Text(title, style: theme.headlineMedium),
    );
  }
}

class _Section extends HookConsumerWidget {
  final String title;
  final Iterable<Widget> children;

  const _Section({
    required this.title,
    required this.children,
  });

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    return SliverList(
      delegate: SliverChildListDelegate([
        const SizedBox(height: 16),
        ...children.toList().reversed,
        _SectionHeader(title: title),
      ]),
    );
  }
}

class _SongsSection extends HookConsumerWidget {
  final IList<Song>? songs;

  const _SongsSection({required this.songs});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final l = AppLocalizations.of(context);

    return _Section(
      title: l.resourcesSongName(100),
      children: (songs ?? <Song>[]).map(
        (song) => QueueContext(
          type: QueueContextType.album,
          id: song.albumId!,
          child: SongListTile(
            song: song,
            image: true,
            onTap: () async {
              const query = ListQuery(
                sort: SortBy(column: 'disc, track'),
              );
              final albumSongs = await ref.read(
                albumSongsListProvider(song.albumId!, query).future,
              );

              ref.read(audioControlProvider).playSongs(
                    context: QueueContextType.album,
                    contextId: song.albumId!,
                    shuffle: true,
                    startIndex: albumSongs.indexOf(song),
                    query: query,
                    getSongs: (query) => ref.read(
                        albumSongsListProvider(song.albumId!, query).future),
                  );
            },
          ),
        ),
      ),
    );
  }
}

class _AlbumsSection extends HookConsumerWidget {
  final IList<Album>? albums;

  const _AlbumsSection({required this.albums});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final l = AppLocalizations.of(context);

    return _Section(
      title: l.resourcesAlbumName(100),
      children: (albums ?? <Album>[]).map(
        (album) => AlbumListTile(
          album: album,
          onTap: () => context.navigateTo(AlbumSongsRoute(id: album.id)),
        ),
      ),
    );
  }
}

class _ArtistsSection extends HookConsumerWidget {
  final IList<Artist>? artists;

  const _ArtistsSection({required this.artists});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final l = AppLocalizations.of(context);

    return _Section(
      title: l.resourcesArtistName(100),
      children: (artists ?? <Artist>[]).map(
        (artist) => ArtistListTile(
          artist: artist,
          onTap: () => context.navigateTo(ArtistRoute(id: artist.id)),
        ),
      ),
    );
  }
}
