import 'dart:async';
import 'dart:io';

import 'package:flutter/foundation.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:window_manager/window_manager.dart';

import '../../../core/infra/player_engine/player_engine.dart';
import '../gestures/gesture_action.dart';
import '../gestures/gesture_bindings.dart';
import 'player_controls_chrome.dart';
import 'player_hold_speed_prompt.dart';

class PlayerVideoWidget extends StatefulWidget {
  final PlayerEngine player;
  final ValueListenable<BoxFit> fit;
  final List<Widget> topBar;
  final Widget bottomBar;
  final Widget? centerBar;
  final List<Widget> overlays;
  final VoidCallback? onControlsAreaTap;
  final bool isFullscreen;
  final Future<void> Function() onToggleFullscreen;
  final bool isPip;
  final Future<void> Function()? onTogglePip;
  final double topBarLeftInset;
  final double topBarTopInset;
  final bool lockControls;
  final bool keyboardEnabled;
  final int seekForwardSeconds;
  final int seekBackwardSeconds;
  final bool volumeBoost;

  final GestureBindings gestureBindings;

  final double currentRate;
  final ValueChanged<double>? onSetRate;
  final VoidCallback? onPrevItem;
  final VoidCallback? onNextItem;
  final VoidCallback? onExit;


  final void Function(Offset globalPosition)? onSecondaryContextMenu;


  final Widget Function(VoidCallback toggleControls, VoidCallback showControls)?
  mobileGestureBuilder;


  final Widget? gestureFeedback;


  final bool suppressControlsChrome;


  final Widget? leftBar;


  final Widget? rightBar;

  const PlayerVideoWidget({
    super.key,
    required this.player,
    required this.fit,
    required this.topBar,
    required this.bottomBar,
    this.centerBar,
    this.overlays = const <Widget>[],
    this.onControlsAreaTap,
    required this.isFullscreen,
    required this.onToggleFullscreen,
    this.isPip = false,
    this.onTogglePip,
    this.topBarLeftInset = 8,
    this.topBarTopInset = 8,
    this.lockControls = false,
    this.keyboardEnabled = true,
    this.seekForwardSeconds = 15,
    this.seekBackwardSeconds = 15,
    this.volumeBoost = false,
    this.gestureBindings = GestureBindings.defaults,
    this.currentRate = 1.0,
    this.onSetRate,
    this.onPrevItem,
    this.onNextItem,
    this.onExit,
    this.onSecondaryContextMenu,
    this.mobileGestureBuilder,
    this.gestureFeedback,
    this.suppressControlsChrome = false,
    this.leftBar,
    this.rightBar,
  });

  @override
  State<PlayerVideoWidget> createState() => _PlayerVideoWidgetState();
}

class _PlayerVideoWidgetState extends State<PlayerVideoWidget> {
  static const double _volumeStep = 5;
  static const double _pipResizeEdgeSize = 10;
  static const List<ResizeEdge> _pipResizeEdges = <ResizeEdge>[
    ResizeEdge.topLeft,
    ResizeEdge.top,
    ResizeEdge.topRight,
    ResizeEdge.left,
    ResizeEdge.right,
    ResizeEdge.bottomLeft,
    ResizeEdge.bottom,
    ResizeEdge.bottomRight,
  ];

  bool _controlsVisible = true;
  bool _cursorVisible = true;
  bool _hoveringControls = false;
  Timer? _controlsHideTimer;
  Timer? _cursorHideTimer;
  StreamSubscription<bool>? _playingSub;
  Offset? _lastMousePosition;

  bool _holdingLeft = false;
  bool _holdingRight = false;
  double? _savedRateBeforeHold;

  bool _holdSpeedPromptVisible = false;
  bool _holdSpeedPromptIsLeft = false;
  double _holdSpeedPromptRate = 1.0;

  Timer? _pendingSeekLeft;
  Timer? _pendingSeekRight;
  static const _arrowHoldThreshold = Duration(milliseconds: 200);


  static const _topHoverTrigger = 120.0;
  static const _bottomHoverTrigger = 180.0;

  Timer? _secondaryTapTimer;
  Offset? _secondaryTapDownPos;

  @override
  void initState() {
    super.initState();
    _playingSub = widget.player.stream.playing.listen((playing) {
      if (!mounted) return;
      if (playing) {
        _scheduleHide();
        _scheduleHideCursor();
      } else {
        _showControls(lock: true);
        _showCursor(lock: true);
      }
    });
    if (widget.player.state.playing) {
      _scheduleHide();
      _scheduleHideCursor();
    }
  }

  @override
  void didUpdateWidget(covariant PlayerVideoWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.player != widget.player) {
      unawaited(_playingSub?.cancel());
      _playingSub = widget.player.stream.playing.listen((playing) {
        if (!mounted) return;
        if (playing) {
          _scheduleHide();
          _scheduleHideCursor();
        } else {
          _showControls(lock: true);
          _showCursor(lock: true);
        }
      });
    }
    if (oldWidget.lockControls != widget.lockControls) {
      if (widget.lockControls) {
        _controlsHideTimer?.cancel();
        _cursorHideTimer?.cancel();
        _showControls(lock: true);
        _showCursor(lock: true);
      } else if (widget.player.state.playing) {
        _scheduleHide();
        _scheduleHideCursor();
      }
    }
    if (oldWidget.keyboardEnabled && !widget.keyboardEnabled) {
      _cancelPendingArrowSeeks();
      _restoreHoldRateIfNeeded();
    }
  }

  @override
  void dispose() {
    _controlsHideTimer?.cancel();
    _cursorHideTimer?.cancel();
    _secondaryTapTimer?.cancel();
    _cancelPendingArrowSeeks();
    _restoreHoldRateIfNeeded();
    unawaited(_playingSub?.cancel());
    super.dispose();
  }

  void _cancelPendingArrowSeeks() {
    _pendingSeekLeft?.cancel();
    _pendingSeekLeft = null;
    _pendingSeekRight?.cancel();
    _pendingSeekRight = null;
  }

  void _showControls({bool lock = false}) {
    _controlsHideTimer?.cancel();
    if (!_controlsVisible && mounted) {
      setState(() => _controlsVisible = true);
    }
    if (!lock && widget.player.state.playing) {
      _scheduleHide();
    }
  }

  void _scheduleHide() {
    _controlsHideTimer?.cancel();
    if (_hoveringControls || widget.lockControls) return;
    _controlsHideTimer = Timer(const Duration(seconds: 3), () {
      if (mounted &&
          widget.player.state.playing &&
          !_hoveringControls &&
          !widget.lockControls) {
        setState(() => _controlsVisible = false);
      }
    });
  }


  void _showCursor({bool lock = false}) {
    _cursorHideTimer?.cancel();
    if (!_cursorVisible && mounted) {
      setState(() => _cursorVisible = true);
    }
    if (!lock && widget.player.state.playing) {
      _scheduleHideCursor();
    }
  }

  void _scheduleHideCursor() {
    _cursorHideTimer?.cancel();
    if (_hoveringControls || widget.lockControls) return;
    _cursorHideTimer = Timer(const Duration(seconds: 3), () {
      if (mounted &&
          widget.player.state.playing &&
          !_hoveringControls &&
          !widget.lockControls) {
        setState(() => _cursorVisible = false);
      }
    });
  }

  bool _inEdgeZone(double localY, double viewHeight) =>
      localY <= _topHoverTrigger || localY >= viewHeight - _bottomHoverTrigger;

  Future<void> _togglePlayPause() async {
    if (widget.player.state.playing) {
      await widget.player.pause();
    } else {
      await widget.player.play();
    }
  }

  KeyEventResult _handleKeyEvent(FocusNode node, KeyEvent event) {
    if (!widget.keyboardEnabled) return KeyEventResult.ignored;
    final key = event.logicalKey;

    if (key == LogicalKeyboardKey.arrowLeft ||
        key == LogicalKeyboardKey.arrowRight) {
      return _handleArrowKey(event, key == LogicalKeyboardKey.arrowLeft);
    }

    final isRepeat = event is KeyRepeatEvent;
    if (event is! KeyDownEvent && !isRepeat) return KeyEventResult.ignored;
    if (key == LogicalKeyboardKey.space) {
      if (isRepeat) return KeyEventResult.handled;
      unawaited(_togglePlayPause());
      _showControls();
      return KeyEventResult.handled;
    }
    if (key == LogicalKeyboardKey.arrowUp) {
      if (Platform.isAndroid) return KeyEventResult.ignored;
      final maxVol = widget.volumeBoost ? 150.0 : 100.0;
      final next = (widget.player.state.volume + _volumeStep).clamp(
        0.0,
        maxVol,
      );
      unawaited(widget.player.setVolume(next));
      _showControls();
      return KeyEventResult.handled;
    }
    if (key == LogicalKeyboardKey.arrowDown) {
      if (Platform.isAndroid) return KeyEventResult.ignored;
      final maxVol = widget.volumeBoost ? 150.0 : 100.0;
      final next = (widget.player.state.volume - _volumeStep).clamp(
        0.0,
        maxVol,
      );
      unawaited(widget.player.setVolume(next));
      _showControls();
      return KeyEventResult.handled;
    }
    if (key == LogicalKeyboardKey.keyF ||
        (key == LogicalKeyboardKey.escape && widget.isFullscreen)) {
      if (isRepeat) return KeyEventResult.handled;
      unawaited(widget.onToggleFullscreen());
      _showControls();
      return KeyEventResult.handled;
    }
    if (key == LogicalKeyboardKey.keyP) {
      if (isRepeat) return KeyEventResult.handled;
      final togglePip = widget.onTogglePip;
      if (togglePip != null) {
        unawaited(togglePip());
        _showControls();
        return KeyEventResult.handled;
      }
    }
    if (key == LogicalKeyboardKey.escape && widget.isPip) {
      if (isRepeat) return KeyEventResult.handled;
      final togglePip = widget.onTogglePip;
      if (togglePip != null) {
        unawaited(togglePip());
        _showControls();
        return KeyEventResult.handled;
      }
    }
    return KeyEventResult.ignored;
  }

  KeyEventResult _handleArrowKey(KeyEvent event, bool isLeft) {
    final seekSeconds = isLeft
        ? -widget.seekBackwardSeconds
        : widget.seekForwardSeconds;

    if (event is KeyDownEvent) {
      if (isLeft) {
        _pendingSeekLeft?.cancel();
        _pendingSeekLeft = Timer(_arrowHoldThreshold, () {
          _pendingSeekLeft = null;
          if (!mounted) return;
          _enterHoldSpeed(isLeft: true);
        });
      } else {
        _pendingSeekRight?.cancel();
        _pendingSeekRight = Timer(_arrowHoldThreshold, () {
          _pendingSeekRight = null;
          if (!mounted) return;
          _enterHoldSpeed(isLeft: false);
        });
      }
      return KeyEventResult.handled;
    }
    if (event is KeyRepeatEvent) {
      return KeyEventResult.handled;
    }
    if (event is KeyUpEvent) {
      final pending = isLeft ? _pendingSeekLeft : _pendingSeekRight;
      if (pending != null) {
        pending.cancel();
        if (isLeft) {
          _pendingSeekLeft = null;
        } else {
          _pendingSeekRight = null;
        }
        _seekBy(seekSeconds);
        _showControls();
      }
      final wasHolding = isLeft ? _holdingLeft : _holdingRight;
      if (wasHolding) {
        if (isLeft) {
          _holdingLeft = false;
        } else {
          _holdingRight = false;
        }
        if (!_holdingLeft && !_holdingRight) {
          _hideHoldSpeedPrompt();
          final restore = _savedRateBeforeHold ?? widget.currentRate;
          _savedRateBeforeHold = null;
          widget.onSetRate?.call(restore);
        }
      }
      return KeyEventResult.handled;
    }
    return KeyEventResult.ignored;
  }

  void _enterHoldSpeed({required bool isLeft}) {
    final holding = isLeft ? _holdingLeft : _holdingRight;
    if (holding) return;

    final holdSpeed = isLeft
        ? widget.gestureBindings.keyboardHoldLeftSpeed
        : widget.gestureBindings.keyboardHoldRightSpeed;
    _savedRateBeforeHold ??= widget.currentRate;
    widget.onSetRate?.call(holdSpeed);
    if (isLeft) {
      _holdingLeft = true;
    } else {
      _holdingRight = true;
    }
    _showHoldSpeedPrompt(isLeft: isLeft, rate: holdSpeed);
  }

  void _showHoldSpeedPrompt({required bool isLeft, required double rate}) {
    if (!mounted) return;
    setState(() {
      _holdSpeedPromptVisible = true;
      _holdSpeedPromptIsLeft = isLeft;
      _holdSpeedPromptRate = rate;
    });
  }

  void _hideHoldSpeedPrompt() {
    if (!_holdSpeedPromptVisible) return;
    if (!mounted) {
      _holdSpeedPromptVisible = false;
      return;
    }
    setState(() => _holdSpeedPromptVisible = false);
  }

  void _restoreHoldRateIfNeeded() {
    if (!_holdingLeft && !_holdingRight) return;
    _holdingLeft = false;
    _holdingRight = false;
    _hideHoldSpeedPrompt();
    final restore = _savedRateBeforeHold;
    _savedRateBeforeHold = null;
    if (restore != null) widget.onSetRate?.call(restore);
  }

  void _seekBy(int seconds) {
    final target = widget.player.state.position + Duration(seconds: seconds);
    unawaited(
      widget.player.seek(target < Duration.zero ? Duration.zero : target),
    );
  }

  void _toggleControlsVisibility() {
    if (_controlsVisible) {
      setState(() => _controlsVisible = false);
      _controlsHideTimer?.cancel();
    } else {
      _showControls();
      _showCursor();
    }
  }

  void _handleSecondaryTap() {
    final contextMenu = widget.onSecondaryContextMenu;
    if (contextMenu != null) {
      contextMenu(_secondaryTapDownPos ?? Offset.zero);
      return;
    }
    if (widget.gestureBindings.mouseRightDoubleClickAction ==
        GestureAction.none) {
      _dispatchGesture(GestureKind.rightClick);
      return;
    }
    final pending = _secondaryTapTimer;
    if (pending != null && pending.isActive) {
      pending.cancel();
      _secondaryTapTimer = null;
      _dispatchGesture(GestureKind.rightDoubleClick);
      return;
    }
    _secondaryTapTimer = Timer(kDoubleTapTimeout, () {
      _secondaryTapTimer = null;
      if (!mounted) return;
      _dispatchGesture(GestureKind.rightClick);
    });
  }

  void _dispatchGesture(GestureKind kind) {
    final action = widget.gestureBindings.actionFor(kind);
    switch (action) {
      case GestureAction.none:
        return;
      case GestureAction.playPause:
        unawaited(_togglePlayPause());
        break;
      case GestureAction.toggleControls:
        _toggleControlsVisibility();
        return;
      case GestureAction.fullscreen:
        unawaited(widget.onToggleFullscreen());
        break;
      case GestureAction.seekBackward:
        _seekBy(-widget.seekBackwardSeconds);
        break;
      case GestureAction.seekForward:
        _seekBy(widget.seekForwardSeconds);
        break;
      case GestureAction.previousItem:
        widget.onPrevItem?.call();
        break;
      case GestureAction.nextItem:
        widget.onNextItem?.call();
        break;
      case GestureAction.exit:
        widget.onExit?.call();
        break;
    }
    _showControls();
    _showCursor();
  }

  Widget _withPipResizeAffordance(Widget child) {
    if (!widget.isPip) return child;
    return DragToResizeArea(
      resizeEdgeSize: _pipResizeEdgeSize,
      resizeEdgeColor: Colors.transparent,
      enableResizeEdges: _pipResizeEdges,
      child: child,
    );
  }

  @override
  Widget build(BuildContext context) {
    final isWindowed = !widget.isFullscreen && !widget.isPip;

    return Focus(
      autofocus: true,
      onKeyEvent: _handleKeyEvent,
      child: Stack(
        fit: StackFit.expand,
        children: [
          Positioned.fill(
            child: LayoutBuilder(
              builder: (context, constraints) {
                final viewHeight = constraints.maxHeight;

                if (widget.mobileGestureBuilder != null) {
                  return _withPipResizeAffordance(
                    Stack(
                      fit: StackFit.expand,
                      children: [
                        PlayerVideoSurface(
                          player: widget.player,
                          fit: widget.fit,
                        ),
                        widget.mobileGestureBuilder!(
                          _toggleControlsVisibility,
                          _showControls,
                        ),
                        ...widget.overlays,
                        if (widget.gestureFeedback != null)
                          widget.gestureFeedback!,
                        HoldSpeedPrompt(
                          visible: _holdSpeedPromptVisible,
                          isLeft: _holdSpeedPromptIsLeft,
                          rate: _holdSpeedPromptRate,
                        ),
                        ControlsChrome(
                          visible:
                              _controlsVisible &&
                              !widget.suppressControlsChrome,
                          topBar: widget.topBar,
                          topBarLeftInset: widget.topBarLeftInset,
                          topBarTopInset: widget.topBarTopInset,
                          bottomBar: widget.bottomBar,
                          centerBar: widget.centerBar,
                          leftBar: widget.leftBar,
                          rightBar: widget.rightBar,
                          onControlsAreaTap: widget.onControlsAreaTap,
                          isWindowed: isWindowed,
                          isPip: widget.isPip,
                          onControlsHoverChanged: (hovering) {
                            _hoveringControls = hovering;
                            if (!hovering && widget.player.state.playing) {
                              _scheduleHide();
                            }
                          },
                        ),
                      ],
                    ),
                  );
                } else {
                  return MouseRegion(
                    cursor: _cursorVisible
                        ? SystemMouseCursors.basic
                        : SystemMouseCursors.none,
                    onEnter: (event) {
                      _lastMousePosition = event.position;
                      _showCursor();
                      if (widget.isPip ||
                          _inEdgeZone(event.localPosition.dy, viewHeight)) {
                        _showControls();
                      }
                    },
                    onExit: (_) {
                      _lastMousePosition = null;
                    },
                    onHover: (event) {
                      final pos = event.position;
                      final last = _lastMousePosition;
                      _lastMousePosition = pos;
                      if (last != null &&
                          _cursorVisible &&
                          _controlsVisible &&
                          (pos - last).distanceSquared < 9) {
                        return;
                      }
                      _showCursor();
                      if (widget.isPip ||
                          _inEdgeZone(event.localPosition.dy, viewHeight)) {
                        _showControls();
                      }
                    },
                    child: GestureDetector(
                      behavior: HitTestBehavior.opaque,
                      onTap: widget.lockControls
                          ? null
                          : () => _dispatchGesture(GestureKind.leftClick),
                      onDoubleTap:
                          widget.lockControls ||
                              widget
                                      .gestureBindings
                                      .mouseLeftDoubleClickAction ==
                                  GestureAction.none
                          ? null
                          : () => _dispatchGesture(GestureKind.leftDoubleClick),
                      onSecondaryTap: widget.lockControls
                          ? null
                          : _handleSecondaryTap,
                      onSecondaryTapDown: widget.lockControls
                          ? null
                          : (d) => _secondaryTapDownPos = d.globalPosition,
                      child: _withPipResizeAffordance(
                        Stack(
                          fit: StackFit.expand,
                          children: [
                            PlayerVideoSurface(
                              player: widget.player,
                              fit: widget.fit,
                            ),
                            ...widget.overlays,
                            HoldSpeedPrompt(
                              visible: _holdSpeedPromptVisible,
                              isLeft: _holdSpeedPromptIsLeft,
                              rate: _holdSpeedPromptRate,
                            ),
                            ControlsChrome(
                              visible:
                                  _controlsVisible &&
                                  !widget.suppressControlsChrome,
                              topBar: widget.topBar,
                              topBarLeftInset: widget.topBarLeftInset,
                              topBarTopInset: widget.topBarTopInset,
                              bottomBar: widget.bottomBar,
                              centerBar: widget.centerBar,
                              leftBar: widget.leftBar,
                              rightBar: widget.rightBar,
                              onControlsAreaTap: widget.onControlsAreaTap,
                              isWindowed: isWindowed,
                              isPip: widget.isPip,
                              onControlsHoverChanged: (hovering) {
                                _hoveringControls = hovering;
                                if (!hovering && widget.player.state.playing) {
                                  _scheduleHide();
                                }
                              },
                            ),
                          ],
                        ),
                      ),
                    ),
                  );
                }
              },
            ),
          ),
        ],
      ),
    );
  }
}

class PlayerVideoSurface extends StatelessWidget {
  final PlayerEngine player;
  final ValueListenable<BoxFit> fit;

  const PlayerVideoSurface({
    super.key,
    required this.player,
    required this.fit,
  });

  @override
  Widget build(BuildContext context) {
    final child = player.buildVideoView(context, fit);
    if (player.usesPlatformSurface) return child;
    return ColoredBox(color: Colors.black, child: child);
  }
}
