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

import '../../../core/contracts/danmaku.dart';
import '../../../core/media/danmaku_episode_parser.dart';
import '../../../shared/utils/format_utils.dart';
import '../../../shared/widgets/app_loading_ring.dart';
import '../services/danmaku/danmaku_session_notifier.dart';

part 'danmaku_search_dialog_view.dart';

const _kBg = Color(0xE8181818);
const _kDivider = Color(0x33FFFFFF);
const _kAccent = Color(0xFF4FA8FF);

enum _Phase {
  idle,
  searching,
  results,
  empty,
  error,
  episodesLoading,
  episodes,
  applying,
}

class DanmakuSearchDialog extends ConsumerStatefulWidget {
  final String currentItemId;
  final bool Function() isItemStillCurrent;
  final String? initialQuery;
  final List<DanmakuMatchCandidate> initialResults;
  final int initialSelectedIndex;
  final int? targetEpisode;

  const DanmakuSearchDialog({
    super.key,
    required this.currentItemId,
    required this.isItemStillCurrent,
    this.initialQuery,
    this.initialResults = const [],
    this.initialSelectedIndex = -1,
    this.targetEpisode,
  });

  @override
  ConsumerState<DanmakuSearchDialog> createState() =>
      _DanmakuSearchDialogState();
}

class _DanmakuSearchDialogState extends ConsumerState<DanmakuSearchDialog> {
  DanmakuSessionNotifier get _notifier =>
      ref.read(danmakuSessionProvider.notifier);

  final TextEditingController _queryCtrl = TextEditingController();
  final FocusNode _focusNode = FocusNode();
  final ScrollController _episodesScrollCtrl = ScrollController();
  final Map<String, List<DanmakuBangumiEpisode>> _episodeCache = {};

  static const double _kEpisodeItemExtent = 68.0;

  _Phase _phase = _Phase.idle;
  List<DanmakuMatchCandidate> _results = const [];
  DanmakuMatchCandidate? _activeMatch;
  List<DanmakuBangumiEpisode> _episodes = const [];
  String? _messageText;
  bool _messageIsError = false;
  int? _flashEpisodeId;
  DanmakuSource? _flashSource;
  int _searchSeq = 0;
  int _episodeSeq = 0;
  int _applySeq = 0;
  Offset _transitionOffset = Offset.zero;

  bool get _busy =>
      _phase == _Phase.searching ||
      _phase == _Phase.episodesLoading ||
      _phase == _Phase.applying;

  String _episodeCacheKey(DanmakuMatchCandidate match) =>
      '${match.source.id}:${match.animeId}';

  @override
  void initState() {
    super.initState();
    final q = widget.initialQuery;
    if (q != null && q.isNotEmpty) {
      _queryCtrl.text = q;
      _queryCtrl.selection = TextSelection.collapsed(offset: q.length);
    }
    if (widget.initialResults.isNotEmpty) {
      _results = widget.initialResults;
      _phase = _Phase.results;
      final idx = widget.initialSelectedIndex;
      if (idx >= 0 && idx < widget.initialResults.length) {
        _flashEpisodeId = widget.initialResults[idx].episodeId;
        _flashSource = widget.initialResults[idx].source;
      }
    }
  }

  @override
  void dispose() {
    _queryCtrl.dispose();
    _focusNode.dispose();
    _episodesScrollCtrl.dispose();
    super.dispose();
  }

  int? _findTargetEpisodeIndex(List<DanmakuBangumiEpisode> episodes) {
    final target = widget.targetEpisode;
    if (target == null) return null;
    for (var i = 0; i < episodes.length; i++) {
      final n = extractEpisodeNumberFromTitle(episodes[i].episodeTitle);
      if (n == target) return i;
    }
    return null;
  }

  void _scheduleEpisodeScroll(int targetIdx) {
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      if (!_episodesScrollCtrl.hasClients) return;
      final position = _episodesScrollCtrl.position;
      final viewport = position.viewportDimension;
      final desired =
          targetIdx * _kEpisodeItemExtent -
          (viewport - _kEpisodeItemExtent) / 2;
      final clamped = desired.clamp(0.0, position.maxScrollExtent);
      _episodesScrollCtrl.jumpTo(clamped);
    });
  }

  void _dismiss() {
    Navigator.of(context, rootNavigator: true).maybePop();
  }

  bool _ensureItemStillCurrent() {
    if (widget.isItemStillCurrent()) return true;
    _dismiss();
    return false;
  }

  Future<void> _submit() async {
    if (!_ensureItemStillCurrent()) return;
    final query = _queryCtrl.text.trim();
    if (query.isEmpty) {
      setState(() {
        _phase = _Phase.idle;
        _messageText = '请输入番剧名';
        _messageIsError = true;
        _flashEpisodeId = null;
      });
      return;
    }

    final seq = ++_searchSeq;
    _episodeSeq++;
    _applySeq++;
    _transitionOffset = Offset.zero;

    setState(() {
      _phase = _Phase.searching;
      _activeMatch = null;
      _episodes = const [];
      _messageText = null;
      _messageIsError = false;
      _flashEpisodeId = null;
    });

    try {
      final matches = await _notifier.manualSearch(query);
      if (!mounted || seq != _searchSeq) return;
      if (matches == null) {
        _dismiss();
        return;
      }
      setState(() {
        _results = matches;
        _phase = matches.isEmpty ? _Phase.empty : _Phase.results;
        _messageText = null;
        _messageIsError = false;
      });
    } catch (_) {
      if (!mounted || seq != _searchSeq) return;
      setState(() {
        _phase = _Phase.error;
        _messageText = '搜索失败，请检查弹幕服务地址';
        _messageIsError = true;
      });
    }
  }

  Future<void> _openAnime(DanmakuMatchCandidate match) async {
    if (_busy) return;
    if (!_ensureItemStillCurrent()) return;
    if (match.animeId <= 0) {
      await _applyDirect(match);
      return;
    }

    final cacheKey = _episodeCacheKey(match);
    final cached = _episodeCache[cacheKey];
    if (cached != null) {
      if (cached.isEmpty) {
        _transitionOffset = const Offset(-0.08, 0);
        setState(() {
          _phase = _Phase.results;
          _activeMatch = null;
          _episodes = const [];
          _messageText = '该番剧未返回分集列表，可直接使用搜索结果"直接加载"';
          _messageIsError = false;
          _flashEpisodeId = null;
        });
        return;
      }
      if (cached.length == 1) {
        _activeMatch = match;
        _episodes = cached;
        await _applyEpisode(match, cached.single);
        return;
      }
      _transitionOffset = const Offset(0.08, 0);
      final targetIdx = _findTargetEpisodeIndex(cached);
      setState(() {
        _activeMatch = match;
        _episodes = cached;
        _phase = _Phase.episodes;
        _messageText = null;
        _messageIsError = false;
        _flashEpisodeId = targetIdx != null
            ? cached[targetIdx].episodeId
            : null;
      });
      if (targetIdx != null) _scheduleEpisodeScroll(targetIdx);
      return;
    }

    final seq = ++_episodeSeq;
    _applySeq++;
    _transitionOffset = const Offset(0.08, 0);

    setState(() {
      _activeMatch = match;
      _episodes = const [];
      _phase = _Phase.episodesLoading;
      _messageText = null;
      _messageIsError = false;
      _flashEpisodeId = null;
    });

    try {
      final episodes = await _notifier.listEpisodesForAnime(
        match.source,
        match.animeId,
      );
      if (!mounted || seq != _episodeSeq) return;
      if (episodes == null) {
        _dismiss();
        return;
      }

      _episodeCache[cacheKey] = episodes;

      if (episodes.isEmpty) {
        _transitionOffset = const Offset(-0.08, 0);
        setState(() {
          _phase = _Phase.results;
          _activeMatch = null;
          _episodes = const [];
          _messageText = '该番剧未返回分集列表，可直接使用搜索结果"直接加载"';
          _messageIsError = false;
        });
        return;
      }

      if (episodes.length == 1) {
        _activeMatch = match;
        _episodes = episodes;
        await _applyEpisode(match, episodes.single);
        return;
      }

      _transitionOffset = const Offset(0.08, 0);
      final targetIdx = _findTargetEpisodeIndex(episodes);
      setState(() {
        _episodes = episodes;
        _phase = _Phase.episodes;
        _messageText = null;
        _messageIsError = false;
        _flashEpisodeId = targetIdx != null
            ? episodes[targetIdx].episodeId
            : null;
      });
      if (targetIdx != null) _scheduleEpisodeScroll(targetIdx);
    } catch (_) {
      if (!mounted || seq != _episodeSeq) return;
      _transitionOffset = const Offset(-0.08, 0);
      setState(() {
        _phase = _Phase.results;
        _activeMatch = null;
        _episodes = const [];
        _messageText = '加载分集失败，请重试';
        _messageIsError = true;
        _flashEpisodeId = null;
      });
    }
  }

  void _backToResults() {
    _episodeSeq++;
    _applySeq++;
    _transitionOffset = const Offset(-0.08, 0);
    setState(() {
      _phase = _Phase.results;
      _activeMatch = null;
      _episodes = const [];
      _flashEpisodeId = null;
    });
  }

  Future<void> _applyEpisode(
    DanmakuMatchCandidate match,
    DanmakuBangumiEpisode episode,
  ) async {
    if (!_ensureItemStillCurrent()) return;
    final seq = ++_applySeq;
    _searchSeq++;
    _episodeSeq++;
    _transitionOffset = Offset.zero;

    setState(() {
      _flashEpisodeId = episode.episodeId;
      _phase = _Phase.applying;
      _messageText = null;
      _messageIsError = false;
    });

    await Future<void>.delayed(const Duration(milliseconds: 100));
    if (!mounted || seq != _applySeq) return;
    if (!_ensureItemStillCurrent()) return;

    try {
      await _notifier.applyManualSelection(
        source: match.source,
        episodeId: episode.episodeId,
        animeId: match.animeId,
        animeTitle: match.animeTitle,
        episodeTitle: episode.episodeTitle,
      );
      if (!mounted || seq != _applySeq) return;
      Navigator.of(context, rootNavigator: true).pop();
    } catch (_) {
      if (!mounted || seq != _applySeq) return;
      setState(() {
        _phase = _episodes.isEmpty ? _Phase.results : _Phase.episodes;
        _messageText = '加载弹幕失败，请重试';
        _messageIsError = true;
        _flashEpisodeId = null;
      });
    }
  }

  Future<void> _applyDirect(DanmakuMatchCandidate match) async {
    if (!_ensureItemStillCurrent()) return;
    final seq = ++_applySeq;
    _searchSeq++;
    _episodeSeq++;
    _transitionOffset = Offset.zero;

    setState(() {
      _flashEpisodeId = match.episodeId;
      _flashSource = match.source;
      _phase = _Phase.applying;
      _messageText = null;
      _messageIsError = false;
    });

    await Future<void>.delayed(const Duration(milliseconds: 100));
    if (!mounted || seq != _applySeq) return;
    if (!_ensureItemStillCurrent()) return;

    try {
      await _notifier.applyManualSelection(
        source: match.source,
        episodeId: match.episodeId,
        animeId: match.animeId,
        animeTitle: match.animeTitle,
        episodeTitle: match.episodeTitle,
      );
      if (!mounted || seq != _applySeq) return;
      Navigator.of(context, rootNavigator: true).pop();
    } catch (_) {
      if (!mounted || seq != _applySeq) return;
      setState(() {
        _phase = _Phase.results;
        _messageText = '加载弹幕失败，请重试';
        _messageIsError = true;
        _flashEpisodeId = null;
        _flashSource = null;
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Shortcuts(
      shortcuts: const <ShortcutActivator, Intent>{
        SingleActivator(LogicalKeyboardKey.escape): DismissIntent(),
      },
      child: Actions(
        actions: <Type, Action<Intent>>{
          DismissIntent: CallbackAction<DismissIntent>(
            onInvoke: (_) {
              _dismiss();
              return null;
            },
          ),
        },
        child: Container(
          clipBehavior: Clip.antiAlias,
          decoration: BoxDecoration(
            color: _kBg,
            borderRadius: BorderRadius.circular(12),
            border: Border.all(color: _kDivider),
            boxShadow: const [
              BoxShadow(
                color: Colors.black54,
                blurRadius: 20,
                offset: Offset(0, 6),
              ),
            ],
          ),
          child: Column(
            mainAxisSize: MainAxisSize.min,
            children: [
              _buildHeader(),
              const FDivider(style: .delta(color: _kDivider, padding: .value(EdgeInsets.zero))),
              Padding(
                padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
                child: _buildSearchField(),
              ),
              const FDivider(style: .delta(color: _kDivider, padding: .value(EdgeInsets.zero))),
              Expanded(
                child: Padding(
                  padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
                  child: AnimatedSwitcher(
                    duration: const Duration(milliseconds: 200),
                    transitionBuilder: _slideFade,
                    child: _buildBody(),
                  ),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
