import 'dart:async';
import 'dart:io';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:flutter_riverpod/misc.dart';
import '../../core/contracts/tmdb.dart';
import '../../providers/di_providers.dart';
import '../../providers/home_page_mode_provider.dart';
import '../../providers/shell_backdrop_provider.dart';
import '../../providers/sm_account_provider.dart';
import '../../providers/tmdb_settings_provider.dart';
import '../../shared/constants/breakpoints.dart';
import '../../shared/mappers/tmdb_image_url.dart';
import '../../shared/utils/color_utils.dart';
import '../../shared/utils/media_nav.dart';
import '../../shared/widgets/app_error_state.dart';
import '../../shared/widgets/empty_state.dart';
import '../../shared/widgets/hover_scrollable_row.dart';
import '../../shared/widgets/media_row_metrics.dart';
import '../../shared/widgets/media_poster_card.dart';
import '../../shared/widgets/scroll_fade_header.dart';
import '../../shared/theme/app_theme.dart';
import '../../shared/widgets/section_header.dart';
import '../../shared/widgets/skeleton_media_row.dart';
import '../../shared/widgets/tmdb_poster_card.dart';
import '../home/widgets/banner_carousel.dart' show HomeBannerSkeleton;
import '../home/widgets/server_dropdown_button.dart';
import 'discover_helpers.dart';
import 'widgets/tmdb_banner_carousel.dart';
import 'widgets/trakt_continue_watching_row.dart';


String _smAccountHint(SmAuthStatus? status) => switch (status) {
  SmAuthStatus.expired => '登录已过期，请重新登录后再浏览推荐内容。',
  SmAuthStatus.degraded => '账号凭据不完整，请重新登录后再浏览推荐内容。',
  _ => '需要登录 sm-server 账号后才能浏览推荐内容。',
};


class _DiscoverRowSpec {
  final String title;
  final String mediaType;
  final String sortBy;
  final String? withGenres;
  final String? voteCountGte;

  const _DiscoverRowSpec({
    required this.title,
    required this.mediaType,
    this.sortBy = 'popularity.desc',
    this.withGenres,
    this.voteCountGte,
  });

  TmdbDiscoverKey get key => (
    mediaType: mediaType,
    sortBy: sortBy,
    withGenres: withGenres,
    voteCountGte: voteCountGte,
  );
}

const _discoverRows = <_DiscoverRowSpec>[
  _DiscoverRowSpec(
    title: '高分剧集',
    mediaType: 'tv',
    sortBy: 'vote_average.desc',
    voteCountGte: '200',
  ),
  _DiscoverRowSpec(title: '科幻电影', mediaType: 'movie', withGenres: '878'),
  _DiscoverRowSpec(title: '动画', mediaType: 'movie', withGenres: '16'),
  _DiscoverRowSpec(title: '真人秀', mediaType: 'tv', withGenres: '10764'),
  _DiscoverRowSpec(title: '纪录片', mediaType: 'movie', withGenres: '99'),
];

class DiscoverPage extends ConsumerStatefulWidget {
  final bool embedInHome;

  const DiscoverPage({super.key, this.embedInHome = false});

  @override
  ConsumerState<DiscoverPage> createState() => _DiscoverPageState();
}

class _DiscoverPageState extends ConsumerState<DiscoverPage> {
  TmdbRecommendation? _lastBannerItem;
  String? _lastBaseUrl;

  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;
  }


  void _onBannerActiveItem(TmdbRecommendation item, String baseUrl) {
    _lastBannerItem = item;
    _lastBaseUrl = baseUrl;
    _writeBackdropIfActive();
  }

  void _writeBackdropIfActive() {
    if (!_isActiveRoute()) return;
    final item = _lastBannerItem;
    final baseUrl = _lastBaseUrl;
    if (item == null || baseUrl == null) return;
    final url = TmdbImageUrl.backdrop(baseUrl, item.backdropPath);
    if (url == null) return;
    final state = ShellBackdropState(
      primaryImageUrl: url,
      fallbackImageUrls: [url],
      accentColor: _accentColorFor(item.id.toString()),
    );
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      if (!_isActiveRoute()) return;
      ref.read(shellBackdropProvider.notifier).show(state);
    });
  }

  bool _isActiveRoute() {
    final location = ref.read(shellLocationProvider);
    if (location == '/discover') return true;
    return widget.embedInHome &&
        location == '/' &&
        ref.read(homePageModeProvider) == HomePageMode.recommend;
  }


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

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

  Future<void> _refresh() async {
    ref.invalidate(tmdbTrendingDayProvider);
    ref.invalidate(tmdbTrendingWeekProvider);
    ref.invalidate(tmdbPopularMoviesProvider);
    ref.invalidate(tmdbPopularTvProvider);
    ref.invalidate(tmdbAiringTodayTvProvider);
    ref.invalidate(tmdbTopRatedMoviesProvider);
    for (final spec in _discoverRows) {
      ref.invalidate(tmdbDiscoverProvider(spec.key));
    }
    ref.invalidate(traktContinueWatchingProvider);
    await Future<void>.delayed(const Duration(milliseconds: 250));
  }

  @override
  Widget build(BuildContext context) {
    final settingsAsync = ref.watch(tmdbSettingsProvider);

    ref.listen<String>(shellLocationProvider, (_, loc) {
      if (loc != '/discover' && !(widget.embedInHome && loc == '/')) return;
      if (_lastBannerItem != null) {
        _writeBackdropIfActive();
      } else {
        _onNoBanner();
      }
    });

    return Scaffold(
      backgroundColor: Colors.transparent,
      extendBodyBehindAppBar: true,
      body: settingsAsync.when(
        loading: () {
          _onNoBanner();
          return const _DiscoverSkeleton();
        },
        error: (_, _) {
          _onNoBanner();
          return AppErrorState(
            title: 'TMDB 设置加载失败',
            onRetry: () => ref.invalidate(tmdbSettingsProvider),
          );
        },
        data: (settings) {
          final accountAsync = ref.watch(smAccountProvider);
          final account = accountAsync.value;
          final needsAccount =
              settings.accessMode == TmdbAccessMode.smAccount &&
              settings.enabled;
          if (needsAccount && account == null && accountAsync.isLoading) {
            _onNoBanner();
            return const _DiscoverSkeleton();
          }
          final canRequest = settings.accessMode == TmdbAccessMode.smAccount
              ? settings.enabled && (account?.hasActiveSession ?? false)
              : settings.canRequest;
          if (!canRequest) {
            _onNoBanner();
            final message = settings.accessMode == TmdbAccessMode.smAccount
                ? _smAccountHint(account?.status)
                : '需要在本地配置 TMDB API Key 或读取令牌后才能浏览推荐内容。';
            return Center(
              child: EmptyState(
                icon: Icons.explore_off_outlined,
                title: 'TMDB 未配置',
                message: message,
              ),
            );
          }

          final sections = <Widget>[
            _DiscoverSection(
              title: '今日热门',
              provider: tmdbTrendingDayProvider,
              imageBaseUrl: settings.effectiveImageBaseUrl,
            ),
            _DiscoverSection(
              title: '本周热门',
              provider: tmdbTrendingWeekProvider,
              imageBaseUrl: settings.effectiveImageBaseUrl,
            ),
            _DiscoverSection(
              title: '热门电影',
              provider: tmdbPopularMoviesProvider,
              imageBaseUrl: settings.effectiveImageBaseUrl,
              fallbackMediaType: 'movie',
            ),
            _DiscoverSection(
              title: '热门剧集',
              provider: tmdbPopularTvProvider,
              imageBaseUrl: settings.effectiveImageBaseUrl,
              fallbackMediaType: 'tv',
            ),
            _DiscoverSection(
              title: '高分电影',
              provider: tmdbTopRatedMoviesProvider,
              imageBaseUrl: settings.effectiveImageBaseUrl,
              fallbackMediaType: 'movie',
            ),
            for (final spec in _discoverRows)
              _DiscoverSection(
                title: spec.title,
                provider: tmdbDiscoverProvider(spec.key),
                imageBaseUrl: settings.effectiveImageBaseUrl,
                fallbackMediaType: spec.mediaType,
              ),
          ];

          final tokens = context.appTokens;
          final isDesktop = Platform.isMacOS || Platform.isWindows;
          final isCompact = Breakpoints.isCompact(
            MediaQuery.sizeOf(context).width,
          );
          final topPad = isDesktop
              ? tokens.titleBarHeight
              : math.max(
                  MediaQuery.paddingOf(context).top,
                  tokens.titleBarHeight,
                );
          final listTopPad = isCompact ? 0.0 : topPad + 30;
          final listBottomPad = widget.embedInHome ? 116.0 : 32.0;

          return Stack(
            children: [
              RefreshIndicator(
                onRefresh: _refresh,
                child: ListView(
                  controller: _scrollController,
                  physics: const AlwaysScrollableScrollPhysics(
                    parent: BouncingScrollPhysics(),
                  ),
                  padding: EdgeInsets.only(
                    top: listTopPad,
                    bottom: listBottomPad,
                  ),
                  children: [
                    Padding(
                      padding: EdgeInsets.only(
                        top: isCompact ? 0 : 12,
                        bottom: 4,
                      ),
                      child: _DiscoverBanner(
                        onActiveItem: _onBannerActiveItem,
                        onNoBanner: _onNoBanner,
                      ),
                    ),
                    const TraktContinueWatchingRow(),
                    ...sections,
                    const SizedBox(height: 24),
                  ],
                ),
              ),
              Positioned(
                top: 0,
                left: 0,
                right: 0,
                child: ScrollFadeHeader(
                  scrollProgress: _scrollProgress,
                  actions: [
                    SizedBox(
                      width: 38,
                      height: 38,
                      child: IconButton(
                        icon: const Icon(Icons.refresh, size: 18),
                        tooltip: '刷新',
                        padding: EdgeInsets.zero,
                        visualDensity: VisualDensity.compact,
                        onPressed: () => unawaited(_refresh()),
                      ),
                    ),
                  ],
                ),
              ),
              if (widget.embedInHome)
                Positioned(
                  top: MediaQuery.paddingOf(context).top + 8,
                  left: 16,
                  child: const ServerDropdownButton(),
                ),
            ],
          );
        },
      ),
    );
  }
}

class _DiscoverSkeleton extends StatelessWidget {
  const _DiscoverSkeleton();

  @override
  Widget build(BuildContext context) {
    final tokens = context.appTokens;
    final isDesktop = Platform.isMacOS || Platform.isWindows;
    final topPad = isDesktop
        ? tokens.titleBarHeight
        : math.max(MediaQuery.paddingOf(context).top, tokens.titleBarHeight);
    final isCompact = Breakpoints.isCompact(MediaQuery.sizeOf(context).width);
    final listTopPad = isCompact ? 0.0 : topPad + 30;

    return ListView(
      physics: const AlwaysScrollableScrollPhysics(
        parent: BouncingScrollPhysics(),
      ),
      padding: EdgeInsets.only(top: listTopPad, bottom: 32),
      children: [
        Padding(
          padding: EdgeInsets.only(top: isCompact ? 0 : 12, bottom: 4),
          child: const HomeBannerSkeleton(),
        ),
        const SkeletonMediaRow(
          title: '继续观看',
          cardVariant: MediaCardVariant.landscape,
          titleColor: Colors.white,
        ),
        const SkeletonMediaRow(title: '今日热门', titleColor: Colors.white),
        const SkeletonMediaRow(title: '本周热门', titleColor: Colors.white),
        const SkeletonMediaRow(title: '热门电影', titleColor: Colors.white),
        const SkeletonMediaRow(title: '热门剧集', titleColor: Colors.white),
      ],
    );
  }
}


class _DiscoverBanner extends ConsumerWidget {

  final void Function(TmdbRecommendation item, String baseUrl) onActiveItem;


  final VoidCallback onNoBanner;

  const _DiscoverBanner({required this.onActiveItem, required this.onNoBanner});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final settings = ref.watch(tmdbSettingsProvider).value;
    final asyncData = ref.watch(tmdbAiringTodayTvProvider);

    return asyncData.when(
      loading: () {
        onNoBanner();
        return const HomeBannerSkeleton();
      },
      error: (_, _) {
        onNoBanner();
        return const SizedBox.shrink();
      },
      data: (res) {
        if (res == null || settings == null) {
          onNoBanner();
          return const SizedBox.shrink();
        }
        final items = res.results
            .where((e) => e.backdropPath?.isNotEmpty ?? false)
            .map((e) => e.mediaType == 'tv' ? e : e.copyWith(mediaType: 'tv'))
            .take(6)
            .toList();
        if (items.isEmpty) {
          onNoBanner();
          return const SizedBox.shrink();
        }

        final baseUrl = settings.effectiveImageBaseUrl;
        return TmdbBannerCarousel(
          items: items,
          imageBaseUrl: baseUrl,
          onActiveItemChanged: (item) => onActiveItem(item, baseUrl),
          onItemTap: (item) => goTmdbDetail(
            context,
            tmdbId: item.id,
            mediaType: 'tv',
            seed: item,
          ),
        );
      },
    );
  }
}

class _DiscoverSection extends ConsumerWidget {
  final String title;
  final ProviderListenable<AsyncValue<TmdbRecommendationsRes?>> provider;
  final String imageBaseUrl;


  final String? fallbackMediaType;

  const _DiscoverSection({
    required this.title,
    required this.provider,
    required this.imageBaseUrl,
    this.fallbackMediaType,
  });

  Widget _sectionTitle() => Padding(
    padding: const EdgeInsets.fromLTRB(24, 16, 24, 4),
    child: SectionHeader(label: title, labelColor: Colors.white),
  );

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final asyncData = ref.watch(provider);

    return asyncData.when(
      loading: () => SkeletonMediaRow(title: title, titleColor: Colors.white),
      error: (_, _) => const SizedBox.shrink(),
      data: (res) {
        if (res == null) return const SizedBox.shrink();

        if (res.results.isEmpty) {
          return Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              _sectionTitle(),
              const EmptyState(
                icon: Icons.movie_filter_outlined,
                title: '暂无内容',
                compact: true,
              ),
            ],
          );
        }

        final items = res.results.length > 20
            ? res.results.sublist(0, 20)
            : res.results;

        final compact = Breakpoints.isCompact(MediaQuery.sizeOf(context).width);
        final cardWidth = MediaRowMetrics.cardWidth(
          MediaCardVariant.poster,
          compact: compact,
        );
        final rowHeight = cardWidth / 0.66 + 46.0;

        return Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            _sectionTitle(),
            SizedBox(
              height: rowHeight,
              child: HoverScrollableRow(
                builder: (_, controller) => ListView.separated(
                  controller: controller,
                  scrollDirection: Axis.horizontal,
                  padding: const EdgeInsets.symmetric(horizontal: 24),
                  itemCount: items.length,
                  separatorBuilder: (_, _) => const SizedBox(width: 14),
                  itemBuilder: (_, i) {
                    final item = items[i];
                    final type = item.mediaType ?? fallbackMediaType;
                    final tappable = type == 'movie' || type == 'tv';
                    return TmdbPosterCard(
                      key: ValueKey<String>('$type:${item.id}'),
                      title: item.title,
                      posterPath: item.posterPath,
                      voteAverage: item.voteAverage,
                      mediaTypeLabel: _mediaTypeLabel(item.mediaType),
                      imageBaseUrl: imageBaseUrl,
                      width: cardWidth,
                      textColor: Colors.white,
                      onTap: tappable
                          ? () => goTmdbDetail(
                              context,
                              tmdbId: item.id,
                              mediaType: type!,
                              seed: item,
                            )
                          : null,
                    );
                  },
                ),
              ),
            ),
          ],
        );
      },
    );
  }

  static String? _mediaTypeLabel(String? type) => mediaTypeLabel(type);
}
