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

import '../../core/contracts/auth.dart';
import '../../core/contracts/library.dart';
import '../../providers/di_providers.dart';
import '../../providers/emby_headers_provider.dart';
import '../../providers/home_banner_provider.dart';
import '../../providers/home_page_mode_provider.dart';
import '../../providers/library_overview_provider.dart';
import '../../providers/playback_active_provider.dart';
import '../../providers/server_provider.dart';
import '../../providers/session_provider.dart';
import '../../providers/shell_backdrop_provider.dart';
import '../../providers/tmdb_prefetch_provider.dart';
import '../../shared/mappers/media_image_url.dart';
import '../../shared/utils/color_utils.dart';
import '../../shared/utils/user_error_formatter.dart';
import '../../shared/widgets/add_server_modal.dart';
import '../../shared/widgets/app_dialog.dart';
import '../../shared/widgets/app_error_state.dart';
import '../../shared/widgets/app_loading_ring.dart';
import '../../shared/widgets/empty_state.dart';
import '../../shared/widgets/error_media_row.dart';
import '../../shared/utils/media_nav.dart';
import '../../shared/widgets/frosted_panel.dart';
import '../../shared/widgets/latest_media_row.dart';
import '../../shared/widgets/login_form.dart';
import '../../shared/widgets/media_poster_card.dart';
import '../../shared/constants/breakpoints.dart';
import '../../shared/widgets/scroll_fade_header.dart';
import '../../shared/widgets/skeleton_media_row.dart';
import '../discover/discover_page.dart';
import 'widgets/banner_carousel.dart';
import 'widgets/server_dropdown_button.dart';

class HomePage extends ConsumerWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    ref.listen<AuthedSession?>(activeSessionProvider, (previous, next) {
      if (previous != null && next == null) {
        ref.read(shellBackdropProvider.notifier).clear();
      }
    });

    final sessionAsync = ref.watch(sessionProvider);
    if (sessionAsync.isLoading && !sessionAsync.hasValue) {
      return const Scaffold(
        backgroundColor: Colors.transparent,
        body: SizedBox.shrink(),
      );
    }

    final session = ref.watch(activeSessionProvider);
    if (session == null) return const _UnauthenticatedHome();
    return _AuthenticatedHome(session: session);
  }
}

class _UnauthenticatedHome extends ConsumerWidget {
  const _UnauthenticatedHome();

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final servers = ref.watch(serverListProvider);
    return Scaffold(
      backgroundColor: Colors.transparent,
      body: servers.when(
        data: (list) {
          final availableList = list.where((s) => !s.paused).toList();

          if (list.isEmpty || availableList.isEmpty) {
            return Center(
              child: EmptyState(
                icon: Icons.cloud_outlined,
                title: list.isEmpty ? '尚未添加 Emby 服务器' : '所有服务器已暂停',
                message: list.isEmpty
                    ? '添加第一个服务器，开始构建你的影库。'
                    : '前往「服务器」页恢复使用，或添加新服务器。',
                action: FButton(
                  variant: FButtonVariant.primary,
                  onPress: () => showAddServerModal(context),
                  child: const Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Icon(Icons.add, size: 16),
                      SizedBox(width: 6),
                      Text('添加服务器'),
                    ],
                  ),
                ),
              ),
            );
          }

          return Center(
            child: SingleChildScrollView(
              padding: const EdgeInsets.all(24),
              child: ConstrainedBox(
                constraints: const BoxConstraints(maxWidth: 540),
                child: FrostedPanel(
                  radius: 32,
                  padding: const EdgeInsets.all(28),
                  color: Theme.of(
                    context,
                  ).colorScheme.surface.withValues(alpha: 0.72),
                  border: Border.all(
                    color: Colors.white.withValues(alpha: 0.22),
                  ),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: [
                      Text(
                        '选择服务器登录',
                        style: Theme.of(context).textTheme.titleLarge,
                      ),
                      const SizedBox(height: 8),
                      Text(
                        '所有内容都会以当前服务器为中心进行同步与推荐。',
                        style: Theme.of(context).textTheme.bodySmall,
                      ),
                      const SizedBox(height: 18),
                      ...availableList.map(
                        (server) => Padding(
                          padding: const EdgeInsets.only(bottom: 10),
                          child: FCard(
                            child: ListTile(
                              leading: const Icon(Icons.dns_outlined),
                              title: Text(server.name),
                              subtitle: Text(server.baseUrl),
                              onTap: () async {
                                await showAppRawDialog<void>(
                                  context,
                                  constraints: const BoxConstraints(
                                    maxWidth: 460,
                                  ),
                                  builder: (_) => Padding(
                                    padding: const EdgeInsets.all(24),
                                    child: LoginForm(
                                      server: server,
                                      onBack: () => Navigator.pop(context),
                                      onSuccess: () => Navigator.pop(context),
                                    ),
                                  ),
                                );
                              },
                            ),
                          ),
                        ),
                      ),
                      const SizedBox(height: 8),
                      FButton(
                        variant: FButtonVariant.outline,
                        onPress: () => showAddServerModal(context),
                        child: const Row(
                          mainAxisSize: MainAxisSize.min,
                          children: [
                            Icon(Icons.add, size: 16),
                            SizedBox(width: 6),
                            Text('添加新服务器'),
                          ],
                        ),
                      ),
                    ],
                  ),
                ),
              ),
            ),
          );
        },
        loading: () => const Center(child: AppLoadingRing()),
        error: (error, _) => AppErrorState(
          message: formatUserError(error, fallback: '登录态加载失败'),
          onRetry: () => ref.invalidate(sessionProvider),
        ),
      ),
    );
  }
}

class _AuthenticatedHome extends ConsumerStatefulWidget {
  final AuthedSession session;

  const _AuthenticatedHome({required this.session});

  @override
  ConsumerState<_AuthenticatedHome> createState() => _AuthenticatedHomeState();
}

class _AuthenticatedHomeState extends ConsumerState<_AuthenticatedHome> {
  final Map<String, bool> _favoriteMap = {};
  final Map<String, bool> _playedMap = {};
  final Map<String, bool> _favoriteLoadingMap = {};
  final Map<String, bool> _playedLoadingMap = {};
  final Map<String, bool> _hideResumeLoadingMap = {};
  final Set<String> _hiddenResumeIds = {};

  final ScrollController _scrollController = ScrollController();
  final ValueNotifier<double> _scrollProgress = ValueNotifier<double>(0.0);

  @override
  void initState() {
    super.initState();
    _scrollController.addListener(_handleScroll);
  }

  @override
  void dispose() {
    _scrollController.removeListener(_handleScroll);
    _scrollController.dispose();
    _scrollProgress.dispose();
    super.dispose();
  }

  void _handleScroll() {
    if (!_scrollController.hasClients) return;
    const threshold = 120.0;
    final progress = (_scrollController.offset / threshold).clamp(0.0, 1.0);
    _scrollProgress.value = progress;
  }

  Future<void> _toggleFavorite(EmbyRawItem item) async {
    if (_favoriteLoadingMap[item.Id] == true) return;
    final next = !(_favoriteMap[item.Id] ?? item.UserData?.IsFavorite ?? false);
    setState(() => _favoriteLoadingMap[item.Id] = true);
    try {
      final useCase = await ref.read(setFavoriteUseCaseProvider.future);
      final result = await useCase.execute(
        FavoriteSetReq(itemId: item.Id, isFavorite: next),
      );
      setState(() => _favoriteMap[item.Id] = result.isFavorite);
    } finally {
      setState(() => _favoriteLoadingMap[item.Id] = false);
    }
  }

  Future<void> _togglePlayed(EmbyRawItem item) async {
    if (_playedLoadingMap[item.Id] == true) return;
    final next = !(_playedMap[item.Id] ?? item.UserData?.Played ?? false);
    setState(() => _playedLoadingMap[item.Id] = true);
    try {
      final useCase = await ref.read(setPlayedUseCaseProvider.future);
      final result = await useCase.execute(
        PlayedSetReq(itemId: item.Id, played: next),
      );
      setState(() => _playedMap[item.Id] = result.played);
      unawaited(_syncTraktPlayedChange(item, result.played));
    } finally {
      setState(() => _playedLoadingMap[item.Id] = false);
    }
  }

  Future<void> _syncTraktPlayedChange(EmbyRawItem item, bool played) async {
    try {
      final service = await ref.read(traktSyncServiceProvider.future);
      await service.syncEmbyPlayedChange(item: item, played: played);
    } catch (_) {}
  }

  Future<void> _hideFromResume(EmbyRawItem item) async {
    if (_hideResumeLoadingMap[item.Id] == true) return;
    setState(() {
      _hideResumeLoadingMap[item.Id] = true;
      _hiddenResumeIds.add(item.Id);
    });
    try {
      final useCase = await ref.read(hideFromResumeUseCaseProvider.future);
      final result = await useCase.execute(
        HideFromResumeReq(itemId: item.Id, hide: true),
      );
      if (!result.hide) {
        setState(() => _hiddenResumeIds.remove(item.Id));
      }
    } catch (_) {
      setState(() => _hiddenResumeIds.remove(item.Id));
    } finally {
      setState(() => _hideResumeLoadingMap[item.Id] = false);
    }
  }

  bool _resolveFavorite(EmbyRawItem item) {
    return _favoriteMap[item.Id] ?? item.UserData?.IsFavorite ?? false;
  }

  bool _resolvePlayed(EmbyRawItem item) {
    return _playedMap[item.Id] ?? item.UserData?.Played ?? false;
  }

  Map<String, bool> _buildFavoriteMap(List<EmbyRawItem> items) {
    return {for (final item in items) item.Id: _resolveFavorite(item)};
  }

  Map<String, bool> _buildPlayedMap(List<EmbyRawItem> items) {
    return {for (final item in items) item.Id: _resolvePlayed(item)};
  }

  void _openMediaDetail(EmbyRawItem item) {
    goMediaDetail(context, item, ref: ref);
  }

  EmbyRawItem? _lastBannerItem;

  bool _isHomeForeground() {
    return ref.read(shellLocationProvider) == '/' &&
        !ref.read(detailPageActiveProvider);
  }

  void _updateShellBackdrop(EmbyRawItem item) {
    _lastBannerItem = item;
    if (!_isHomeForeground()) return;

    final backdropUrl = EmbyImageUrl.detailBanner(
      baseUrl: widget.session.serverUrl,
      token: widget.session.token,
      item: item,
    );
    final fallbackId = (item.SeriesId?.isNotEmpty ?? false)
        ? item.SeriesId!
        : item.Id;
    final fallbackUrls = <String>{
      EmbyImageUrl.fanart(
        baseUrl: widget.session.serverUrl,
        token: widget.session.token,
        itemId: fallbackId,
      ),
      backdropUrl,
    }.toList();
    final accentColor = _accentColorFor(item.Id);

    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      if (!_isHomeForeground()) return;
      ref
          .read(shellBackdropProvider.notifier)
          .show(
            ShellBackdropState(
              primaryImageUrl: backdropUrl,
              fallbackImageUrls: fallbackUrls,
              accentColor: accentColor,
            ),
          );
    });
  }

  void _clearShellBackdropDeferred() {
    _lastBannerItem = null;
    if (ref.read(shellBackdropProvider) == null) return;
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      if (!_isHomeForeground()) return;
      ref.read(shellBackdropProvider.notifier).clear();
    });
  }

  Color _accentColorFor(String seed) => accentColorForSeed(
    seed,
    isDark: Theme.of(context).brightness == Brightness.dark,
  );

  @override
  Widget build(BuildContext context) {
    final isCompact = MediaQuery.sizeOf(context).width < Breakpoints.compact;
    final mode = ref.watch(homePageModeProvider);
    if (isCompact && mode == HomePageMode.recommend) {
      return const DiscoverPage(embedInHome: true);
    }

    final state = ref.watch(libraryOverviewProvider);
    final bannerAsync = ref.watch(homeBannerProvider);
    final imageHeaders = ref.watch(embyImageHeadersProvider).value;
    final dpr = MediaQuery.devicePixelRatioOf(context);
    final homeForeground =
        ref.watch(shellLocationProvider) == '/' &&
        !ref.watch(detailPageActiveProvider);
    ref.watch(tmdbPrefetchProvider);

    ref.listen<String>(shellLocationProvider, (_, loc) {
      if (loc != '/') return;
      if (ref.read(detailPageActiveProvider)) return;
      if (_lastBannerItem != null) {
        _updateShellBackdrop(_lastBannerItem!);
      } else {
        _clearShellBackdropDeferred();
      }
    });
    ref.listen<bool>(detailPageActiveProvider, (_, active) {
      if (active) return;
      if (ref.read(shellLocationProvider) != '/') return;
      ref.read(libraryOverviewProvider.notifier).softRefresh();
      if (_lastBannerItem != null) {
        _updateShellBackdrop(_lastBannerItem!);
      } else {
        _clearShellBackdropDeferred();
      }
    });

    return Scaffold(
      backgroundColor: Colors.transparent,
      body: state.when(
        data: (data) {
          if (data.libraries.isEmpty) {
            _clearShellBackdropDeferred();
            return const Center(
              child: EmptyState(
                icon: Icons.movie_filter_outlined,
                title: '没有可用的媒体库',
              ),
            );
          }

          final resumeItems = data.resumeItems
              .where((item) => !_hiddenResumeIds.contains(item.Id))
              .toList();

          return Stack(
            children: [
              RefreshIndicator(
                onRefresh: () => Future.wait([
                  ref.read(libraryOverviewProvider.notifier).softRefresh(),
                  ref.read(homeBannerProvider.notifier).softRefresh(),
                ]),
                displacement: 72,
                child: ListView(
                  controller: _scrollController,
                  physics: const AlwaysScrollableScrollPhysics(
                    parent: BouncingScrollPhysics(),
                  ),
                  padding: EdgeInsets.only(top: isCompact ? 0 : 60, bottom: 32),
                  children: [
                    bannerAsync.when(
                      data: (bannerItems) {
                        if (bannerItems.isEmpty) {
                          _clearShellBackdropDeferred();
                          return const SizedBox.shrink(
                            key: ValueKey('home-banner-empty'),
                          );
                        }
                        return Padding(
                          key: const ValueKey('home-banner'),
                          padding: const EdgeInsets.only(bottom: 16),
                          child: HomeBannerCarousel(
                            baseUrl: widget.session.serverUrl,
                            token: widget.session.token,
                            items: bannerItems,
                            imageHeaders: imageHeaders,
                            onItemTap: _openMediaDetail,
                            onActiveItemChanged: _updateShellBackdrop,
                            active: homeForeground,
                          ),
                        );
                      },
                      loading: () => const Padding(
                        key: ValueKey('home-banner-loading'),
                        padding: EdgeInsets.only(bottom: 16),
                        child: HomeBannerSkeleton(),
                      ),
                      error: (_, _) {
                        _clearShellBackdropDeferred();
                        return const SizedBox.shrink(
                          key: ValueKey('home-banner-error'),
                        );
                      },
                    ),
                    if (data.resumeLoading)
                      const SkeletonMediaRow(
                        key: ValueKey('home-resume-row'),
                        title: '继续观看',
                        cardVariant: MediaCardVariant.landscape,
                        titleColor: Colors.white,
                      )
                    else if (data.resumeError != null)
                      ErrorMediaRow(
                        key: const ValueKey('home-resume-row'),
                        title: '继续观看',
                        cardVariant: MediaCardVariant.landscape,
                        titleColor: Colors.white,
                        onRetry: () => ref
                            .read(libraryOverviewProvider.notifier)
                            .retryResume(),
                      )
                    else if (resumeItems.isNotEmpty)
                      LatestMediaRow(
                        key: const ValueKey('home-resume-row'),
                        title: '继续观看',
                        items: resumeItems,
                        cardVariant: MediaCardVariant.landscape,
                        enableContextMenu: true,
                        favoriteMap: _buildFavoriteMap(resumeItems),
                        playedMap: _buildPlayedMap(resumeItems),
                        favoriteLoadingMap: _favoriteLoadingMap,
                        playedLoadingMap: _playedLoadingMap,
                        onAddFavorite: _toggleFavorite,
                        onMarkPlayed: _togglePlayed,
                        onHideFromResume: _hideFromResume,
                        hideFromResumeLoadingMap: _hideResumeLoadingMap,
                        imageHeaders: imageHeaders,
                        titleColor: Colors.white,
                        imageUrlBuilder: (item) => EmbyImageUrl.landscapeCard(
                          baseUrl: widget.session.serverUrl,
                          token: widget.session.token,
                          item: item,
                          maxWidth: EmbyImageUrl.targetPixelWidth(232, dpr),
                        ),
                        preloadImageUrlBuilder: (item) =>
                            EmbyImageUrl.detailBanner(
                              baseUrl: widget.session.serverUrl,
                              token: widget.session.token,
                              item: item,
                            ),
                        onItemTap: _openMediaDetail,
                      ),
                    if (data.libraries.isNotEmpty)
                      LatestMediaRow(
                        key: const ValueKey('home-library-row'),
                        title: '我的媒体列表',
                        items: data.libraries
                            .map(
                              (library) => EmbyRawItem(
                                Id: library.Id,
                                Name: library.Name,
                                PrimaryImageTag: library.ImageTags?['Primary'],
                                ImageTags: library.ImageTags,
                              ),
                            )
                            .toList(),
                        cardVariant: MediaCardVariant.landscape,
                        showHoverPlayIcon: false,
                        imageHeaders: imageHeaders,
                        titleColor: Colors.white,
                        imageUrlBuilder: (item) => EmbyImageUrl.primary(
                          baseUrl: widget.session.serverUrl,
                          token: widget.session.token,
                          item: item,
                          maxWidth: EmbyImageUrl.targetPixelWidth(232, dpr),
                        ),
                        onItemTap: (item) =>
                            context.push('/library/${item.Id}'),
                      ),
                    ...data.libraries.expand((library) {
                      final isLoading = data.loadingLibraryIds.contains(
                        library.Id,
                      );
                      if (isLoading) {
                        return [
                          SkeletonMediaRow(
                            key: ValueKey('home-latest-row-${library.Id}'),
                            title: '${library.Name} · 最新更新',
                            titleColor: Colors.white,
                          ),
                        ];
                      }
                      final error = data.latestErrorByLibrary[library.Id];
                      if (error != null) {
                        return [
                          ErrorMediaRow(
                            key: ValueKey('home-latest-row-${library.Id}'),
                            title: '${library.Name} · 最新更新',
                            titleColor: Colors.white,
                            onRetry: () => ref
                                .read(libraryOverviewProvider.notifier)
                                .retryLatest(library.Id),
                          ),
                        ];
                      }
                      final items =
                          data.latestByLibrary[library.Id] ??
                          const <EmbyRawItem>[];
                      if (items.isEmpty) return const <Widget>[];
                      return [
                        LatestMediaRow(
                          key: ValueKey('home-latest-row-${library.Id}'),
                          title: library.Name,
                          subtitle: '最新更新',
                          items: items,
                          totalCount: data.totalCountByLibrary[library.Id],
                          enableContextMenu: true,
                          favoriteMap: _buildFavoriteMap(items),
                          playedMap: _buildPlayedMap(items),
                          favoriteLoadingMap: _favoriteLoadingMap,
                          playedLoadingMap: _playedLoadingMap,
                          onAddFavorite: _toggleFavorite,
                          onMarkPlayed: _togglePlayed,
                          imageHeaders: imageHeaders,
                          titleColor: Colors.white,
                          imageUrlBuilder: (item) => EmbyImageUrl.primary(
                            baseUrl: widget.session.serverUrl,
                            token: widget.session.token,
                            item: item,
                            maxWidth: EmbyImageUrl.targetPixelWidth(158, dpr),
                          ),
                          preloadImageUrlBuilder: (item) =>
                              EmbyImageUrl.detailBanner(
                                baseUrl: widget.session.serverUrl,
                                token: widget.session.token,
                                item: item,
                              ),
                          onItemTap: _openMediaDetail,
                          onSeeAll: () =>
                              context.push('/library/${library.Id}'),
                        ),
                      ];
                    }),
                  ],
                ),
              ),
              if (MediaQuery.sizeOf(context).width < Breakpoints.compact)
                Positioned(
                  top: MediaQuery.paddingOf(context).top + 8,
                  left: 16,
                  child: const ServerDropdownButton(),
                )
              else
                Positioned(
                  top: 0,
                  left: 0,
                  right: 0,
                  child: ScrollFadeHeader(scrollProgress: _scrollProgress),
                ),
            ],
          );
        },
        loading: () => const Center(child: AppLoadingRing()),
        error: (error, _) => AppErrorState(
          message: formatUserError(error, fallback: '首页内容加载失败'),
          panel: true,
          onRetry: () => ref.read(libraryOverviewProvider.notifier).refresh(),
        ),
      ),
    );
  }
}
