import 'dart:math' as math;
import 'package:fis_theme/theme.dart';
import 'package:fis_ui/index.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

typedef bool FFormItemValidateStepFn();

/// 表单项验证步骤
class FFormItemValidateStep {
  FFormItemValidateStep({
    required this.validateFn,
    required this.tips,
  });
  FFormItemValidateStepFn validateFn;
  String tips;
}

/// FFormItemValidator控制器
class FFormItemValidatorController extends ChangeNotifier {
  bool _isDisposed = false;
  bool _isShowing = false;
  bool _isValidating = false;

  /// 是否在显示
  bool get isShowing => _isShowing;

  /// 是否在验证中
  bool get isValidating => _isValidating;

  /// 需要显示自定义错误提示
  bool get needShowCustom => customErrorText.isNotEmpty;

  /// 自定义错误提示
  String customErrorText = '';

  @override
  void addListener(VoidCallback listener) {
    if (_isDisposed) return;
    super.addListener(listener);
  }

  @override
  void removeListener(VoidCallback listener) {
    if (_isDisposed) return;
    super.removeListener(listener);
  }

  /// 重置验证
  void resetValidate() {
    if (_isDisposed) return;
    _isValidating = false;
    _isShowing = true;
    notifyListeners();
  }

  /// 开始验证
  void validate() {
    if (_isDisposed) return;
    _isShowing = true;
    _isValidating = true;
    notifyListeners();
  }

  /// 隐藏
  void hide() {
    if (_isDisposed) return;
    _isShowing = false;
    _isValidating = false;
    notifyListeners();
  }

  /// 展示手动控制的错误提示
  void showCustomError(String text) {
    if (_isDisposed) return;
    customErrorText = text;
    notifyListeners();
  }

  /// 移除手动控制的错误提示
  void removeCustomError() {
    if (_isDisposed) return;
    customErrorText = '';
    notifyListeners();
  }

  @override
  void dispose() {
    _isDisposed = true;
    super.dispose();
  }
}

/// 表单项验证组件
class FFormItemValidator extends StatefulWidget implements FWidget {
  const FFormItemValidator({
    Key? key,
    required this.child,
    required this.controller,
    this.validateSteps = const [],
    this.overlayContext,
  }) : super(key: key);

  final FWidget child;
  final FFormItemValidatorController controller;
  final List<FFormItemValidateStep> validateSteps;
  final BuildContext? overlayContext;

  @override
  State<StatefulWidget> createState() => _FFormItemValidatorState();
}

class _FFormItemValidatorState extends State<FFormItemValidator> {
  OverlayEntry? _overlayEntry;
  late RenderBox _childBox;
  late RenderBox _parentBox;
  late Offset _childOffset;
  final _arrowColor = Colors.black.withOpacity(.9);
  static const _arrowSize = 10.0;
  static const _verticalMargin = 0.0;
  static const _horizontalMargin = 0.0;
  bool _isShowCustomError = false;

  BuildContext get _parentContext =>
      widget.overlayContext ?? Overlay.of(context).context;

  @override
  void initState() {
    super.initState();
    widget.controller.addListener(_updateView);
    WidgetsBinding.instance.addPostFrameCallback((call) {
      if (mounted) {
        _childBox = context.findRenderObject() as RenderBox;
        _parentBox = _parentContext.findRenderObject() as RenderBox;
        _childOffset = _childBox.localToGlobal(Offset.zero);
      }
    });
  }

  @override
  FWidget build(BuildContext context) {
    return widget.child;
  }

  @override
  void dispose() {
    _hideView();
    widget.controller.removeListener(_updateView);
    super.dispose();
  }

  void _updateView() {
    if (widget.controller.isShowing) {
      _hideView();
      _isShowCustomError = false;
      _showView();
    } else {
      _hideView();
      if (widget.controller.needShowCustom) {
        _isShowCustomError = true;
        _showView();
      }
    }
  }

  void _showView() {
    if (_overlayEntry == null) {
      if (mounted) {
        _childBox = context.findRenderObject() as RenderBox;
        _parentBox = _parentContext.findRenderObject() as RenderBox;
        _childOffset = _childBox.localToGlobal(Offset.zero);
      }
      _buildOverlayEntry();
      final ctx = widget.overlayContext ?? context;
      Overlay.of(ctx).insert(_overlayEntry!);
    }
  }

  void _hideView() {
    if (_overlayEntry != null) {
      _overlayEntry?.remove();
      _overlayEntry = null;
    }
  }

  void _buildOverlayEntry() {
    _overlayEntry = OverlayEntry(
      builder: (context) {
        return IgnorePointer(
          child: FContainer(
            constraints: BoxConstraints(
              maxWidth: _parentBox.size.width - 2 * _horizontalMargin,
              maxHeight: _parentBox.size.height -
                  2 * _verticalMargin -
                  _childOffset.dy -
                  _childBox.size.height,
            ),
            child: _buildTooltipLayout(),
          ),
        );
      },
    );
  }

  FWidget _buildTooltipConent() {
    return FContainer(
      padding: const EdgeInsets.all(10.0),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(8.0),
        color: _arrowColor,
      ),
      width: _childBox.size.width - 2 * _horizontalMargin,
      child: FColumn(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: widget.validateSteps
            .map(
              (e) => _ValidateStepWidget(
                controller: widget.controller,
                data: e,
              ),
            )
            .toList(),
      ),
    );
  }

  /// 自定义错误提示
  FWidget _buildCustomTooltipConent() {
    return FContainer(
      padding: const EdgeInsets.all(10.0),
      decoration: BoxDecoration(
        borderRadius: BorderRadius.circular(8.0),
        color: _arrowColor,
      ),
      width: _childBox.size.width - 2 * _horizontalMargin,
      child: FColumn(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _CustomValidateStepWidget(text: widget.controller.customErrorText)
        ],
      ),
    );
  }

  FWidget _buildTooltipLayout() {
    final arrow = ClipPath(
      child: FContainer(
        width: _arrowSize,
        height: _arrowSize,
        color: _arrowColor,
      ),
      clipper: _ArrowClipper(),
    );
    final content = _isShowCustomError
        ? _buildCustomTooltipConent()
        : _buildTooltipConent();

    return FCustomMultiChildLayout(
      delegate: _LayoutDelegate(
        anchorSize: _childBox.size,
        anchorOffset: _childOffset,
        verticalMargin: _verticalMargin,
        horizontalMargin: _horizontalMargin,
      ),
      children: <FWidget>[
        FLayoutId(
          id: _LayoutId.downArrow,
          child: Transform.rotate(
            angle: math.pi,
            child: arrow,
          ),
        ),
        FLayoutId(
          id: _LayoutId.content,
          child: FColumn(
            mainAxisSize: MainAxisSize.min,
            children: <FWidget>[
              FMaterial(
                child: content,
                color: Colors.transparent,
              ),
            ],
          ),
        ),
      ],
    );
  }
}

enum _LayoutId {
  downArrow,
  content,
}

class _LayoutDelegate extends MultiChildLayoutDelegate {
  _LayoutDelegate({
    required this.anchorSize,
    required this.anchorOffset,
    required this.verticalMargin,
    required this.horizontalMargin,
  });

  final Size anchorSize;
  final Offset anchorOffset;
  final double verticalMargin;
  final double horizontalMargin;

  @override
  void performLayout(Size size) {
    Size contentSize = Size.zero;
    Size arrowSize = Size.zero;
    Offset contentOffset = Offset.zero;
    Offset arrowOffset = Offset.zero;

    double anchorTopY = anchorOffset.dy;

    if (hasChild(_LayoutId.content)) {
      contentSize = layoutChild(
        _LayoutId.content,
        BoxConstraints.loose(size),
      );
    }
    if (hasChild(_LayoutId.downArrow)) {
      arrowSize = layoutChild(
        _LayoutId.downArrow,
        BoxConstraints.loose(size),
      );
    }

    arrowOffset = Offset(
      anchorOffset.dx +
          anchorSize.width -
          horizontalMargin * 2 -
          arrowSize.width * 2,
      anchorTopY - verticalMargin - arrowSize.height,
    );
    contentOffset = Offset(
      anchorOffset.dx + horizontalMargin,
      anchorTopY - verticalMargin - arrowSize.height - contentSize.height,
    );

    if (hasChild(_LayoutId.content)) {
      positionChild(_LayoutId.content, contentOffset);
    }
    if (hasChild(_LayoutId.downArrow)) {
      positionChild(
        _LayoutId.downArrow,
        Offset(arrowOffset.dx, arrowOffset.dy - 0.1),
      );
    }
  }

  @override
  bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false;
}

class _ArrowClipper extends CustomClipper<Path> {
  @override
  Path getClip(Size size) {
    Path path = Path();
    path.moveTo(0, size.height);
    path.lineTo(size.width / 2, size.height / 2);
    path.lineTo(size.width, size.height);
    return path;
  }

  @override
  bool shouldReclip(CustomClipper<Path> oldClipper) {
    return true;
  }
}

class _ValidateStepWidget extends FStatefulWidget {
  _ValidateStepWidget({
    Key? key,
    required this.controller,
    required this.data,
  }) : super(key: key);

  final FFormItemValidatorController controller;
  final FFormItemValidateStep data;

  @override
  FState<_ValidateStepWidget> createState() => _ValidateStepWidgetState();
}

class _ValidateStepWidgetState extends FState<_ValidateStepWidget> {
  late final TextStyle _textStyle;
  static const Color _initialColor = Colors.white;
  static const Color _rightColor = Color(0xFF66BB6A);
  static const Color _errorColor = Color(0xFFEF5350);

  @override
  void initState() {
    _textStyle = TextStyle(
      fontFamily: FTheme.ins.localeSetting.fontFamily,
      fontSize: 13.sp,
      color: _initialColor,
    );

    super.initState();

    widget.controller.addListener(_updateView);
  }

  @override
  FWidget build(BuildContext context) {
    if (!widget.controller.isValidating) {
      return FText(widget.data.tips, style: _textStyle);
    }
    final checked = widget.data.validateFn();
    final color = checked ? _rightColor : _errorColor;

    return FRow(
      children: [
        FIcon(
          checked ? Icons.check_rounded : Icons.close_rounded,
          color: color,
          size: 13.sp,
        ),
        FExpanded(
          child: FText(
            widget.data.tips,
            style: _textStyle.copyWith(
              color: color,
            ),
          ),
        ),
      ],
    );
  }

  void _updateView() {
    if (widget.controller.isShowing) {
      setState(() {});
    }
  }

  @override
  void dispose() {
    widget.controller.removeListener(_updateView);
    super.dispose();
  }
}

class _CustomValidateStepWidget extends FStatelessWidget {
  _CustomValidateStepWidget({
    Key? key,
    required this.text,
  }) : super(key: key);

  final String text;
  final _textStyle = TextStyle(
    fontFamily: FTheme.ins.localeSetting.fontFamily,
    fontSize: 13.sp,
    color: Color(0xFFEF5350),
  );

  @override
  FWidget build(BuildContext context) {
    return FRow(
      children: [
        FIcon(
          Icons.close_rounded,
          color: Color(0xFFEF5350),
          size: 13.sp,
        ),
        FExpanded(
          child: FText(text, style: _textStyle),
        ),
      ],
    );
  }
}
