// lib/widgets/filter_grids/albums_page.dart
import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/covers.dart';
import 'package:aves/model/dynamic_albums.dart';
import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/filters/container/album_group.dart';
import 'package:aves/model/filters/container/dynamic_album.dart';
import 'package:aves/model/filters/covered/stored_album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/grouping/common.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/album.dart';
import 'package:aves/model/source/collection_source.dart';
import 'package:aves/remote/remote_origin.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:aves/widgets/common/identity/empty.dart';
import 'package:aves/widgets/common/providers/filter_group_provider.dart';
import 'package:aves/widgets/filter_grids/common/action_delegates/album_set.dart';
import 'package:aves/widgets/filter_grids/common/enums.dart';
import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart';
import 'package:aves/widgets/filter_grids/common/section_keys.dart';
import 'package:aves_model/aves_model.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

class AlbumListPage extends StatelessWidget {
  static const routeName = '/albums';

  final Uri? initialGroup;

  const AlbumListPage({
    super.key,
    required this.initialGroup,
  });

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider<FilterGrouping>.value(value: albumGrouping),
        FilterGroupProvider(initialValue: initialGroup),
      ],
      child: Builder(
        builder: (context) {
          final source = context.read<CollectionSource>();

          return Selector<
              Settings,
              (
                AlbumChipSectionFactor,
                ChipSortFactor,
                bool,
                Set<CollectionFilter>,
                Set<CollectionFilter>,
              )>(
            selector: (context, s) => (
              s.albumSectionFactor,
              s.albumSortFactor,
              s.albumSortReverse,
              s.hiddenFilters,
              s.pinnedFilters,
            ),
            shouldRebuild: (t1, t2) {
              const eq = DeepCollectionEquality();

              return !(
                eq.equals(t1.$1, t2.$1) &&
                eq.equals(t1.$2, t2.$2) &&
                eq.equals(t1.$3, t2.$3) &&
                eq.equals(t1.$4, t2.$4) &&
                eq.equals(t1.$5, t2.$5)
              );
            },
            builder: (context, s, child) {
              return ValueListenableBuilder<bool>(
                valueListenable: appInventory.areAppNamesReadyNotifier,
                builder: (context, areAppNamesReady, child) {
                  return AnimatedBuilder(
                    animation: Listenable.merge({
                      albumGrouping,
                      dynamicAlbums,
                    }),
                    builder: (context, child) => StreamBuilder(
                      stream: source.eventBus.on<AlbumsChangedEvent>(),
                      builder: (context, snapshot) {
                        final groupUri =
                            context.watch<FilterGroupNotifier>().value;

                        final gridItems = getGridItems(
                          context,
                          source,
                          AlbumChipType.values,
                          groupUri,
                        );

                        debugPrint(
                          '[albums_page] build '
                          'gridItems=${gridItems.length} '
                          'folderMap=${source.folderMap.length} '
                          'visibleEntries=${source.visibleEntries.length}',
                        );

                        return StreamBuilder<Set<CollectionFilter>?>(
                          stream: covers.packageChangeStream,
                          builder: (context, snapshot) =>
                              FilterNavigationPage<CollectionFilter,
                                  AlbumChipSetActionDelegate>(
                            source: source,
                            title: context.l10n.albumPageTitle,
                            sortFactor: settings.albumSortFactor,
                            showHeaders: settings.albumSectionFactor !=
                                AlbumChipSectionFactor.none,

                            // Mostra icona remote nella app bar Album.
                            showRemoteAction: true,

                            actionDelegate: AlbumChipSetActionDelegate(
                              gridItems,
                            ),
                            filterSections: groupToSections(
                              context,
                              source,
                              gridItems,
                            ),
                            newFilters: source.getNewAlbumFilters(context),
                            emptyBuilder: () => EmptyContent(
                              icon: AIcons.album,
                              text: context.l10n.albumEmpty,
                            ),
                          ),
                        );
                      },
                    ),
                  );
                },
              );
            },
          );
        },
      ),
    );
  }

  // Common with picking page.
  //
  // FolderMap-only:
  // - album list viene solo da source.folderMap
  // - UI resta compatibile Aves usando StoredAlbumFilter
  // - nessuna dedup per basename
  // - se più album visibili hanno stesso displayName: Nome*1, Nome*2, ...
  static List<FilterGridItem<CollectionFilter>> getGridItems(
    BuildContext context,
    CollectionSource source,
    Iterable<AlbumChipType> albumChipTypes,
    Uri? groupUri,
  ) {
    final groupContent = albumGrouping.getDirectChildren(groupUri);

    Set<T> whereTypeRecursively<T>(Set<CollectionFilter> filters) {
      return {
        ...filters.whereType<T>(),
        ...filters
            .whereType<AlbumGroupFilter>()
            .expand((v) => whereTypeRecursively<T>(v.filter.innerFilters)),
      };
    }

    final pinned = settings.pinnedFilters.whereType<CollectionFilter>();
    final hidden = settings.hiddenFilters.whereNot(pinned.contains).toSet();

    final listedStoredAlbums = <StoredAlbumFilter>{};

    bool isRemoteFolderPath(String path) {
      return path.startsWith('/photos/');
    }

    bool isRemoteFolderStats(dynamic f) {
      return f.isRemote == RemoteOrigin.value || isRemoteFolderPath(f.path);
    }

    bool shouldShowFolder(dynamic f) {
      if (f.hidden == 1) return false;
      if (!source.remoteVisible && isRemoteFolderStats(f)) return false;

      return true;
    }

    if (albumChipTypes.contains(AlbumChipType.stored)) {
      final folderStats = source.folderMap.values
          .where(shouldShowFolder)
          .toList();

      final remoteFolders = source.folderMap.values
          .where(isRemoteFolderStats)
          .length;

      debugPrint(
        '[albums_page] remote filter '
        'remoteVisible=${source.remoteVisible} '
        'folderMap=${source.folderMap.length} '
        'remoteFolders=$remoteFolders '
        'shownFolders=${folderStats.length}',
      );

      // ------------------------------------------------------------
      // Display name disambiguation.
      //
      // Se più album visibili hanno lo stesso displayName:
      // Camera*1
      // Camera*2
      //
      // Se invece il nome è unico:
      // Camera
      // ------------------------------------------------------------

      final nameBuckets = <String, List<String>>{};

      for (final stats in folderStats) {
        final name = stats.displayName;
        nameBuckets.putIfAbsent(name, () => <String>[]).add(stats.path);
      }

      final displayNameByPath = <String, String>{};

      for (final entry in nameBuckets.entries) {
        final name = entry.key;
        final paths = entry.value..sort();

        if (paths.length == 1) {
          displayNameByPath[paths.first] = name;
        } else {
          for (var i = 0; i < paths.length; i++) {
            displayNameByPath[paths[i]] = '$name*${i + 1}';
          }
        }
      }

      String displayNameForFolder(String path) {
        return displayNameByPath[path] ??
            source.folderMap[path]?.displayName ??
            source.getStoredAlbumDisplayName(context, path);
      }

      final allFolderPaths = folderStats.map((f) => f.path).toSet();

      debugPrint(
        '[albums_page] getGridItems folderMap-only '
        'folderMap=${source.folderMap.length} '
        'folders=${folderStats.length} '
        'groupUri=$groupUri',
      );

      if (groupUri == null) {
        // Root:
        // esclude gli album già presenti nei gruppi configurati.
        // Qui usiamo il path esatto, non basename, così non c'è dedup.
        final withinGroupPaths =
            whereTypeRecursively<StoredAlbumFilter>(groupContent)
                .map((v) => v.album)
                .toSet();

        for (final stats in folderStats) {
          if (withinGroupPaths.contains(stats.path)) continue;

          final filter = StoredAlbumFilter(
            stats.path,
            displayNameForFolder(stats.path),
          );

          if (hidden.contains(filter)) continue;

          listedStoredAlbums.add(filter);
        }
      } else {
        // Dentro un gruppo:
        // mostra solo gli album dichiarati nel gruppo e ancora presenti in folderMap.
        for (final groupedAlbum
            in groupContent.whereType<StoredAlbumFilter>()) {
          final albumPath = groupedAlbum.album;

          if (!allFolderPaths.contains(albumPath)) continue;

          final stats = source.folderMap[albumPath];

          if (stats == null) continue;
          if (!shouldShowFolder(stats)) continue;

          final filter = StoredAlbumFilter(
            albumPath,
            displayNameForFolder(albumPath),
          );

          if (hidden.contains(filter)) continue;

          listedStoredAlbums.add(filter);
        }
      }

      debugPrint(
        '[albums_page] display names '
        'duplicates=${nameBuckets.entries.where((e) => e.value.length > 1).length} '
        'stored=${listedStoredAlbums.length}',
      );
    }

    final listedDynamicAlbums = <DynamicAlbumFilter>{};

    if (albumChipTypes.contains(AlbumChipType.dynamic)) {
      final allDynamicAlbums =
          dynamicAlbums.all.whereNot(hidden.contains).toSet();

      if (groupUri == null) {
        final withinGroups =
            whereTypeRecursively<DynamicAlbumFilter>(groupContent).toSet();

        listedDynamicAlbums.addAll(
          allDynamicAlbums.whereNot(withinGroups.contains),
        );
      } else {
        listedDynamicAlbums.addAll(
          groupContent
              .whereType<DynamicAlbumFilter>()
              .where(allDynamicAlbums.contains),
        );
      }
    }

    // Sempre mostra i gruppi, necessari per navigare nei contenuti.
    final albumGroupFilters = groupContent
        .whereType<AlbumGroupFilter>()
        .whereNot(hidden.contains)
        .toSet();

    final filters = <CollectionFilter>{
      ...albumGroupFilters,
      ...listedStoredAlbums,
      ...listedDynamicAlbums,
    };

    debugPrint(
      '[albums_page] getGridItems filters '
      'stored=${listedStoredAlbums.length} '
      'dynamic=${listedDynamicAlbums.length} '
      'groups=${albumGroupFilters.length} '
      'total=${filters.length}',
    );

    final sorted = FilterNavigationPage.sort(
      settings.albumSortFactor,
      settings.albumSortReverse,
      source,
      filters,
    );

    debugPrint(
      '[albums_page] getGridItems sorted=${sorted.length}',
    );

    return sorted;
  }

  static Map<ChipSectionKey, List<FilterGridItem<CollectionFilter>>>
      groupToSections(
    BuildContext context,
    CollectionSource source,
    Iterable<FilterGridItem<CollectionFilter>> sortedMapEntries,
  ) {
    final newFilters = source.getNewAlbumFilters(context);
    final pinned = settings.pinnedFilters.whereType<CollectionFilter>();

    final newMapEntries = <FilterGridItem<CollectionFilter>>[];
    final pinnedMapEntries = <FilterGridItem<CollectionFilter>>[];
    final unpinnedMapEntries = <FilterGridItem<CollectionFilter>>[];

    for (final item in sortedMapEntries) {
      final filter = item.filter;

      if (newFilters.contains(filter)) {
        newMapEntries.add(item);
      } else if (pinned.contains(filter)) {
        pinnedMapEntries.add(item);
      } else {
        unpinnedMapEntries.add(item);
      }
    }

    var sections = <ChipSectionKey, List<FilterGridItem<CollectionFilter>>>{};

    switch (settings.albumSectionFactor) {
      case AlbumChipSectionFactor.importance:
        final groupKey = AlbumImportanceSectionKey.group(context);
        final specialKey = AlbumImportanceSectionKey.special(context);
        final appsKey = AlbumImportanceSectionKey.apps(context);
        final vaultKey = AlbumImportanceSectionKey.vault(context);
        final regularKey = AlbumImportanceSectionKey.regular(context);
        final dynamicKey = AlbumImportanceSectionKey.dynamic(context);

        sections = groupBy<FilterGridItem<CollectionFilter>, ChipSectionKey>(
          unpinnedMapEntries,
          (kv) {
            final filter = kv.filter;

            switch (filter) {
              case StoredAlbumFilter _:
                switch (covers.effectiveAlbumType(filter.album)) {
                  case AlbumType.regular:
                    return regularKey;
                  case AlbumType.app:
                    return appsKey;
                  case AlbumType.vault:
                    return vaultKey;
                  default:
                    return specialKey;
                }

              case DynamicAlbumFilter _:
                return dynamicKey;

              case AlbumGroupFilter _:
                return groupKey;
            }

            return specialKey;
          },
        );

        sections = {
          if (sections.containsKey(groupKey)) groupKey: sections[groupKey]!,
          if (sections.containsKey(specialKey))
            specialKey: sections[specialKey]!,
          if (sections.containsKey(appsKey)) appsKey: sections[appsKey]!,
          if (sections.containsKey(vaultKey)) vaultKey: sections[vaultKey]!,
          if (sections.containsKey(dynamicKey))
            dynamicKey: sections[dynamicKey]!,
          if (sections.containsKey(regularKey))
            regularKey: sections[regularKey]!,
        };

      case AlbumChipSectionFactor.mimeType:
        final visibleEntries = source.visibleEntries;

        sections = groupBy<FilterGridItem<CollectionFilter>, ChipSectionKey>(
          unpinnedMapEntries,
          (kv) {
            final matches = visibleEntries.where(kv.filter.test);
            final hasImage = matches.any((v) => v.isImage);
            final hasVideo = matches.any((v) => v.isVideo);

            if (hasImage && !hasVideo) return MimeTypeSectionKey.images(context);
            if (!hasImage && hasVideo) return MimeTypeSectionKey.videos(context);

            return MimeTypeSectionKey.mixed(context);
          },
        );

      case AlbumChipSectionFactor.volume:
        sections = groupBy<FilterGridItem<CollectionFilter>, ChipSectionKey>(
          unpinnedMapEntries,
          (kv) {
            final filter = kv.filter;

            return StorageVolumeSectionKey(
              context,
              filter is StoredAlbumFilter ? filter.storageVolume : null,
            );
          },
        );

      case AlbumChipSectionFactor.none:
        return {
          if (sortedMapEntries.isNotEmpty)
            const ChipSectionKey(): [
              ...newMapEntries,
              ...pinnedMapEntries,
              ...unpinnedMapEntries,
            ],
        };
    }

    if (pinnedMapEntries.isNotEmpty) {
      sections = Map.fromEntries([
        MapEntry(
          AlbumImportanceSectionKey.pinned(context),
          pinnedMapEntries,
        ),
        ...sections.entries,
      ]);
    }

    if (newMapEntries.isNotEmpty) {
      sections = Map.fromEntries([
        MapEntry(
          AlbumImportanceSectionKey.newAlbum(context),
          newMapEntries,
        ),
        ...sections.entries,
      ]);
    }

    debugPrint(
      '[albums_page] groupToSections '
      'sectionFactor=${settings.albumSectionFactor} '
      'new=${newMapEntries.length} '
      'pinned=${pinnedMapEntries.length} '
      'unpinned=${unpinnedMapEntries.length} '
      'sections=${sections.length}',
    );

    return sections;
  }
}
