// SPDX-FileCopyrightText: 2026 FocusFlow contributors
// SPDX-License-Identifier: AGPL-3.0-only

part of '../task_selection_modal.dart';

/// Menu destinations exposed by the modal Push button.
enum _ModalPushDestination {
  /// Pushes the task to the next available slot.
  next,

  /// Pushes the task to tomorrow's first available slot.
  tomorrow,

  /// Pushes the task to Backlog.
  backlog,
}

/// Private implementation type for `_ActionButton` in this library.
/// It keeps supporting state and behavior scoped to the owning file so the public API stays focused on the scheduler concepts that callers use directly.
class _ActionButton extends StatefulWidget {
  /// Creates a `_ActionButton` instance with the values required by its invariants.
  /// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
  const _ActionButton({required this.icon, required this.label, this.onTap});

  /// Stores the `icon` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final IconData icon;

  /// Stores the `label` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final String label;

  /// Callback invoked when the action is tapped.
  final VoidCallback? onTap;

  /// Creates the mutable state object used by this stateful widget.
  /// Flutter calls this once for each mounted widget instance before lifecycle callbacks and builds begin.
  @override
  State<_ActionButton> createState() => _ActionButtonState();
}

/// Private implementation type for `_ActionButtonState` in this library.
/// It owns hover feedback for compact modal action buttons.
class _ActionButtonState extends State<_ActionButton> {
  /// Whether the pointer is hovering this action button.
  bool _isHovered = false;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    return _ModalActionSurface(
      icon: widget.icon,
      label: widget.label,
      isHovered: _isHovered,
      onHoverChanged: (value) {
        setState(() {
          _isHovered = value;
        });
      },
      onTap: widget.onTap ?? () {},
    );
  }
}

/// Private implementation type for `_PushActionButton` in this library.
/// It exposes push destinations from the clicked-task modal.
class _PushActionButton extends StatefulWidget {
  /// Creates a `_PushActionButton` instance with the values required by its invariants.
  /// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
  const _PushActionButton({
    required this.card,
    this.onPushToNext,
    this.onPushToTomorrow,
    this.onPushToBacklog,
  });

  /// Stores the `card` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final TimelineCardModel card;

  /// Callback invoked when Push to next is selected.
  final Future<void> Function(TimelineCardModel card)? onPushToNext;

  /// Callback invoked when Push to tomorrow is selected.
  final Future<void> Function(TimelineCardModel card)? onPushToTomorrow;

  /// Callback invoked when Push to backlog is selected.
  final Future<void> Function(TimelineCardModel card)? onPushToBacklog;

  /// Creates the mutable state object used by this stateful widget.
  /// Flutter calls this once for each mounted widget instance before lifecycle callbacks and builds begin.
  @override
  State<_PushActionButton> createState() => _PushActionButtonState();
}

/// Private implementation type for `_PushActionButtonState` in this library.
/// It owns hover and running state for modal push destinations.
class _PushActionButtonState extends State<_PushActionButton> {
  /// Whether the pointer is hovering this action button.
  bool _isHovered = false;

  /// Whether a push destination command is in flight.
  bool _isRunning = false;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    final options = _modalPushDestinationOptions();
    final enabled =
        !_isRunning && options.any((option) => option.callback != null);
    return PopupMenuButton<_ModalPushDestination>(
      key: ValueKey('${widget.card.id}-modal-push-button'),
      tooltip: '',
      enabled: enabled,
      color: const Color(0xFF122018),
      elevation: 8,
      position: PopupMenuPosition.under,
      onSelected: (destination) {
        for (final option in options) {
          if (option.destination != destination) {
            continue;
          }
          final callback = option.callback;
          if (callback != null) {
            unawaited(_runPush(callback));
          }
          return;
        }
      },
      itemBuilder: (context) => [
        for (final option in options)
          PopupMenuItem<_ModalPushDestination>(
            value: option.destination,
            enabled: option.callback != null,
            child: Text(option.label),
          ),
      ],
      child: _ModalActionSurface(
        icon: Icons.arrow_forward,
        label: 'Push',
        isHovered: _isHovered,
        isEnabled: enabled,
        onHoverChanged: (value) {
          setState(() {
            _isHovered = value;
          });
        },
      ),
    );
  }

  /// Builds the push destination option list for this button.
  List<_ModalPushDestinationOption> _modalPushDestinationOptions() {
    return [
      _ModalPushDestinationOption(
        destination: _ModalPushDestination.next,
        label: 'Push to next',
        callback: widget.onPushToNext == null
            ? null
            : () => widget.onPushToNext!(widget.card),
      ),
      _ModalPushDestinationOption(
        destination: _ModalPushDestination.tomorrow,
        label: 'Push to tomorrow',
        callback: widget.onPushToTomorrow == null
            ? null
            : () => widget.onPushToTomorrow!(widget.card),
      ),
      _ModalPushDestinationOption(
        destination: _ModalPushDestination.backlog,
        label: 'Push to backlog',
        callback: widget.onPushToBacklog == null
            ? null
            : () => widget.onPushToBacklog!(widget.card),
      ),
    ];
  }

  /// Runs [callback] while guarding against duplicate push commands.
  Future<void> _runPush(Future<void> Function() callback) async {
    if (_isRunning) {
      return;
    }
    setState(() {
      _isRunning = true;
    });
    try {
      await callback();
    } finally {
      if (mounted) {
        setState(() {
          _isRunning = false;
        });
      }
    }
  }
}

/// Private implementation type for `_ModalPushDestinationOption` in this library.
/// It stores one modal push menu destination.
class _ModalPushDestinationOption {
  /// Creates a `_ModalPushDestinationOption` instance with the values required by its invariants.
  /// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
  const _ModalPushDestinationOption({
    required this.destination,
    required this.label,
    required this.callback,
  });

  /// Stores the `destination` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final _ModalPushDestination destination;

  /// Stores the `label` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final String label;

  /// Callback invoked when this option is selected.
  final Future<void> Function()? callback;
}

/// Private implementation type for `_ModalActionSurface` in this library.
/// It draws compact modal action button chrome shared by push and placeholder actions.
class _ModalActionSurface extends StatelessWidget {
  /// Creates a `_ModalActionSurface` instance with the values required by its invariants.
  /// Callers should pass already-normalized scheduler data because validation and defaulting belong at the model or service boundary, not in arbitrary UI code.
  const _ModalActionSurface({
    required this.icon,
    required this.label,
    required this.isHovered,
    required this.onHoverChanged,
    this.isEnabled = true,
    this.onTap,
  });

  /// Stores the `icon` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final IconData icon;

  /// Stores the `label` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final String label;

  /// Stores the `isHovered` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final bool isHovered;

  /// Stores the `isEnabled` value for this object.
  /// This field is part of the explicit state that downstream scheduling, persistence, or UI code reads when composing behavior.
  final bool isEnabled;

  /// Callback invoked when the hover state changes.
  final ValueChanged<bool> onHoverChanged;

  /// Callback invoked when the action surface is tapped.
  final VoidCallback? onTap;

  /// Builds the widget subtree for this component.
  /// The method translates the current widget configuration and state into Flutter UI without mutating scheduler domain data.
  @override
  Widget build(BuildContext context) {
    final foreground = isEnabled
        ? FocusFlowTokens.textPrimary
        : FocusFlowTokens.textMuted;
    final hoverColor = FocusFlowTokens.textPrimary.withValues(alpha: 0.08);
    return Semantics(
      button: true,
      enabled: isEnabled,
      label: label,
      child: MouseRegion(
        cursor: isEnabled ? SystemMouseCursors.click : SystemMouseCursors.basic,
        onEnter: (_) => onHoverChanged(true),
        onExit: (_) => onHoverChanged(false),
        child: GestureDetector(
          behavior: HitTestBehavior.opaque,
          onTap: isEnabled ? onTap : null,
          child: AnimatedContainer(
            height: 42,
            padding: const EdgeInsets.symmetric(horizontal: 14),
            duration: const Duration(milliseconds: 120),
            curve: Curves.easeOut,
            decoration: BoxDecoration(
              color: isHovered && isEnabled
                  ? hoverColor
                  : FocusFlowTokens.glassPanelStrong.withValues(alpha: 0.26),
              borderRadius: BorderRadius.circular(
                FocusFlowTokens.smallButtonRadius,
              ),
              border: Border.all(
                color: isHovered && isEnabled
                    ? FocusFlowTokens.textPrimary.withValues(alpha: 0.22)
                    : FocusFlowTokens.subtleBorder.withValues(alpha: 0.72),
              ),
              boxShadow: isHovered && isEnabled
                  ? [
                      BoxShadow(
                        color: FocusFlowTokens.textPrimary.withValues(
                          alpha: 0.12,
                        ),
                        blurRadius: 12,
                        spreadRadius: 1,
                      ),
                    ]
                  : const [],
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.start,
              children: [
                SizedBox(
                  width: 28,
                  child: Align(
                    alignment: Alignment.centerLeft,
                    child: Icon(icon, color: foreground, size: 18),
                  ),
                ),
                const SizedBox(width: 8),
                Flexible(
                  child: Text(
                    label,
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis,
                    style: TextStyle(
                      color: foreground,
                      fontSize: 15,
                      fontWeight: FontWeight.w700,
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
