// tab_swipe.dart — Tab 左右滑动切换的手势仲裁组件
//
// 约定(design/CLAUDE.md §5):横向位移 >60px 且明显大于纵向时切换 Tab;
// 子页内的纵向滚动区域不响应该手势。
//
// 仲裁机制:本组件只接 HorizontalDrag。纵向滚动由内部 ListView 的
// VerticalDragGestureRecognizer 在手势竞技场中胜出,本组件的横向识别器
// 自然落败——因此「滚动列表/纵向拖拽」永远不会误触发 Tab 切换,无需手写
// dx/dy 比例判断。横向为主的拖拽才会进入本组件并按阈值判定方向。
import 'package:flutter/material.dart';

/// 方向感知:1 = 向后(进入右侧 Tab),-1 = 向前(进入左侧 Tab)。
typedef SwipeCallback = void Function();

class HorizontalSwipeArea extends StatefulWidget {
  const HorizontalSwipeArea({
    super.key,
    required this.child,
    this.onSwipeLeft,
    this.onSwipeRight,
    this.threshold = 60,
  });

  /// 包裹的内容(通常是当前 Tab 页)。
  final Widget child;

  /// 向左滑(手指从右往左)→ 切到下一个 Tab。
  final VoidCallback? onSwipeLeft;

  /// 向右滑(手指从左往右)→ 切到上一个 Tab。
  final VoidCallback? onSwipeRight;

  /// 触发阈值(逻辑像素)。
  final double threshold;

  @override
  State<HorizontalSwipeArea> createState() => _HorizontalSwipeAreaState();
}

class _HorizontalSwipeAreaState extends State<HorizontalSwipeArea> {
  double _dx = 0;

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      // 仅接横向拖拽:纵向滚动交给内部可滚动组件(竞技场仲裁)。
      onHorizontalDragStart: (_) => _dx = 0,
      onHorizontalDragUpdate: (d) => _dx += d.delta.dx,
      onHorizontalDragEnd: (details) {
        final v = details.primaryVelocity ?? 0;
        // 位移过阈值,或带明显速度的快划。
        final left = _dx <= -widget.threshold || v < -300;
        final right = _dx >= widget.threshold || v > 300;
        if (left) {
          widget.onSwipeLeft?.call();
        } else if (right) {
          widget.onSwipeRight?.call();
        }
        _dx = 0;
      },
      child: widget.child,
    );
  }
}

/// 方向感知的页面切换动画:200ms 轻移淡入(对齐 React pg-in-l / pg-in-r)。
class DirectionalTabSwitcher extends StatelessWidget {
  const DirectionalTabSwitcher({super.key, required this.index, required this.direction, required this.child});

  /// 当前 Tab 索引(作为切换 key)。
  final int index;

  /// 进入方向:1 从右进(下一个),-1 从左进(上一个),0 无动画。
  final int direction;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return AnimatedSwitcher(
      duration: const Duration(milliseconds: 200),
      switchInCurve: PangolinMotionEaseOut.curve,
      transitionBuilder: (child, animation) {
        // 仅对「进入」的新页做方向位移 + 淡入;旧页直接淡出,避免回弹。
        final isIncoming = child.key == ValueKey<int>(index);
        if (direction == 0 || !isIncoming) {
          return FadeTransition(opacity: animation, child: child);
        }
        final begin = Offset(direction > 0 ? 0.06 : -0.06, 0);
        return FadeTransition(
          opacity: animation,
          child: SlideTransition(
            position: Tween<Offset>(begin: begin, end: Offset.zero).animate(animation),
            child: child,
          ),
        );
      },
      layoutBuilder: (currentChild, previousChildren) => Stack(
        alignment: Alignment.topCenter,
        children: [...previousChildren, if (currentChild != null) currentChild],
      ),
      child: KeyedSubtree(key: ValueKey<int>(index), child: child),
    );
  }
}

/// 暴露 easeOut 曲线(避免在本文件 import 主题造成循环)。
class PangolinMotionEaseOut {
  PangolinMotionEaseOut._();
  static const Curve curve = Cubic(0.22, 1, 0.36, 1);
}
