import 'package:flutter/material.dart';

/// 品牌化下拉刷新组件
///
/// 图标固定在 AppBar 下方 64px 区域内居中；
/// 内容区用 Transform.translate 下移（仅 paint-time offset，不触发 layout），
/// 与参考视频效果一致。
///
/// 兼容 iOS（BouncingScrollPhysics）和 Android（ClampingScrollPhysics）。
class AppRefreshIndicator extends StatefulWidget {
  const AppRefreshIndicator({
    super.key,
    required this.onRefresh,
    required this.child,
  });

  final Future<void> Function() onRefresh;
  final Widget child;

  @override
  State<AppRefreshIndicator> createState() => _AppRefreshIndicatorState();
}

enum _PtrState { idle, pulling, ready, loading }

class _AppRefreshIndicatorState extends State<AppRefreshIndicator>
    with TickerProviderStateMixin {
  static const double _kThreshold = 64.0;
  static const double _kMaxPull = 110.0;

  late final AnimationController _spinCtrl;
  late final AnimationController _collapseCtrl;
  late Animation<double> _collapseAnim;

  _PtrState _ptrState = _PtrState.idle;
  double _pullHeight = 0.0;
  bool _tracking = false;
  double _bounceScale = 1.0;
  bool _bouncePlayed = false;

  @override
  void initState() {
    super.initState();
    _spinCtrl = AnimationController(
      vsync: this,
      duration: const Duration(milliseconds: 800),
    );
    _collapseCtrl = AnimationController(vsync: this);
    _collapseAnim = const AlwaysStoppedAnimation(0.0);
    _collapseCtrl.addListener(() {
      if (mounted) setState(() {});
    });
  }

  @override
  void dispose() {
    _spinCtrl.dispose();
    _collapseCtrl.dispose();
    super.dispose();
  }

  double get _displayHeight {
    if (_ptrState == _PtrState.loading) return _kThreshold;
    if (_collapseCtrl.isAnimating) return _collapseAnim.value;
    return _pullHeight;
  }

  void _animateCollapse({required double from}) {
    _collapseAnim = Tween<double>(begin: from, end: 0).animate(
      CurvedAnimation(
        parent: _collapseCtrl,
        curve: const Cubic(0.34, 1.20, 0.64, 1.00),
      ),
    );
    _collapseCtrl.duration = const Duration(milliseconds: 360);
    _collapseCtrl.forward(from: 0);
  }

  bool _handleNotification(ScrollNotification notification) {
    if (_ptrState == _PtrState.loading) return false;

    if (notification is ScrollStartNotification) {
      if (notification.metrics.extentBefore == 0.0) {
        _collapseCtrl.stop();
        _tracking = true;
      }
      return false;
    }

    if (notification is ScrollUpdateNotification && _tracking) {
      if (notification.metrics.extentBefore > 0.0) {
        _tracking = false;
        if (_pullHeight > 0) _collapse();
        return false;
      }
      final raw = -(notification.scrollDelta ?? 0.0);
      if (raw > 0) {
        final resistance = 1.0 - (_pullHeight / _kMaxPull).clamp(0.0, 1.0);
        _updatePullHeight(_pullHeight + raw * resistance * 0.55);
      } else if (raw < 0) {
        _updatePullHeight((_pullHeight + raw).clamp(0.0, _kMaxPull));
      }
      return false;
    }

    if (notification is OverscrollNotification &&
        notification.overscroll < 0 &&
        _tracking) {
      final raw = -notification.overscroll;
      final resistance = 1.0 - (_pullHeight / _kMaxPull).clamp(0.0, 1.0);
      _updatePullHeight(_pullHeight + raw * resistance * 0.55);
      return false;
    }

    if (notification is ScrollEndNotification && _tracking) {
      _tracking = false;
      if (_ptrState == _PtrState.ready) {
        _startLoading();
      } else if (_pullHeight > 0) {
        _collapse();
      }
      return false;
    }

    return false;
  }

  void _updatePullHeight(double h) {
    final newH = h.clamp(0.0, _kMaxPull);
    final wasReady = _ptrState == _PtrState.ready;
    final nowReady = newH >= _kThreshold;
    setState(() {
      _pullHeight = newH;
      _ptrState = nowReady ? _PtrState.ready : _PtrState.pulling;
    });
    if (nowReady && !wasReady && !_bouncePlayed) {
      _bouncePlayed = true;
      _playBounce();
    }
    if (!nowReady && wasReady) {
      _bouncePlayed = false;
    }
  }

  Future<void> _playBounce() async {
    const steps = 15;
    for (var i = 0; i <= steps; i++) {
      await Future.delayed(const Duration(milliseconds: 10));
      if (!mounted || _ptrState != _PtrState.ready) return;
      final t = i / steps;
      setState(() {
        _bounceScale =
            t <= 0.5 ? 1.0 + 0.15 * (t * 2) : 1.15 - 0.15 * ((t - 0.5) * 2);
      });
    }
    if (mounted) setState(() => _bounceScale = 1.0);
  }

  void _collapse() {
    final from = _pullHeight;
    setState(() {
      _ptrState = _PtrState.idle;
      _pullHeight = 0;
      _bouncePlayed = false;
    });
    _animateCollapse(from: from);
  }

  Future<void> _startLoading() async {
    setState(() {
      _ptrState = _PtrState.loading;
      _pullHeight = 0;
      _bouncePlayed = false;
    });
    _spinCtrl.repeat();
    try {
      await widget.onRefresh();
    } finally {
      if (mounted) _endLoading();
    }
  }

  void _endLoading() {
    _spinCtrl
      ..stop()
      ..reset();
    setState(() {
      _ptrState = _PtrState.idle;
      _bounceScale = 1.0;
    });
    _animateCollapse(from: _kThreshold);
  }

  @override
  Widget build(BuildContext context) {
    final h = _displayHeight;
    final opacity = (h / _kThreshold).clamp(0.0, 1.0);
    final isLoading = _ptrState == _PtrState.loading;

    return NotificationListener<OverscrollIndicatorNotification>(
      onNotification: (n) {
        if (_ptrState != _PtrState.idle) n.disallowIndicator();
        return true;
      },
      child: NotificationListener<ScrollNotification>(
        onNotification: _handleNotification,
        child: Stack(
          clipBehavior: Clip.hardEdge,
          children: [
            // ── 图标层（固定在顶部，不随内容移动）─────────────
            Positioned(
              top: 0,
              left: 0,
              right: 0,
              height: _kThreshold,
              child: Center(
                child: Opacity(
                  opacity: opacity,
                  child: Transform.scale(
                    scale: _bounceScale,
                    child: isLoading
                        ? RotationTransition(
                            turns: _spinCtrl,
                            child: Image.asset(
                              'assets/images/app_icon.png',
                              width: 32,
                              height: 32,
                            ),
                          )
                        : Image.asset(
                            'assets/images/app_icon.png',
                            width: 32,
                            height: 32,
                          ),
                  ),
                ),
              ),
            ),
            // ── 内容层（Transform.translate 下移，不触发 layout）─
            Transform.translate(
              offset: Offset(0, h),
              child: widget.child,
            ),
          ],
        ),
      ),
    );
  }
}
