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

/// Renders the Required Banner widget for the FocusFlow Flutter UI.
library;

import 'package:flutter/material.dart';

import '../models/today_screen_models.dart';
import '../theme/focus_flow_tokens.dart';

/// Banner that highlights the next required task, if one exists.
class RequiredBanner extends StatelessWidget {
  /// Creates a required-task banner.
  const RequiredBanner({this.model, this.onShowUpcoming, super.key});

  /// Optional required task model to render.
  final RequiredBannerModel? model;

  /// Callback invoked when the highlighted required task should be shown.
  final VoidCallback? onShowUpcoming;

  /// 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 banner = model;
    if (banner == null) {
      return const SizedBox.shrink();
    }
    return LayoutBuilder(
      builder: (context, constraints) {
        final metrics = _RequiredBannerMetrics.fromWidth(constraints.maxWidth);
        return Container(
          height: _RequiredBannerMetrics.referenceHeight,
          padding: EdgeInsets.symmetric(horizontal: metrics.horizontalPadding),
          decoration: BoxDecoration(
            color: FocusFlowTokens.glassPanel,
            borderRadius: BorderRadius.circular(metrics.bannerRadius),
            border: Border.all(
              color: FocusFlowTokens.navBorder.withValues(alpha: 0.55),
              width: metrics.borderWidth,
            ),
          ),
          child: Row(
            children: [
              Container(
                width: metrics.iconCircleSize,
                height: metrics.iconCircleSize,
                decoration: const BoxDecoration(
                  shape: BoxShape.circle,
                  color: FocusFlowTokens.glassPanelStrong,
                ),
                child: Icon(
                  Icons.auto_awesome,
                  color: FocusFlowTokens.accentMagenta,
                  size: metrics.iconSize,
                ),
              ),
              SizedBox(width: metrics.iconGap),
              Expanded(
                child: Align(
                  alignment: Alignment.centerLeft,
                  child: FittedBox(
                    alignment: Alignment.centerLeft,
                    fit: BoxFit.scaleDown,
                    child: RichText(
                      text: TextSpan(
                        style: TextStyle(
                          color: FocusFlowTokens.textPrimary,
                          fontSize: metrics.textSize,
                          fontWeight: FontWeight.w700,
                        ),
                        children: [
                          const TextSpan(text: 'Next required task:   '),
                          TextSpan(
                            text: banner.title,
                            style: const TextStyle(
                              color: FocusFlowTokens.accentMagenta,
                            ),
                          ),
                          TextSpan(
                            text: '  at ${banner.timeText}',
                            style: TextStyle(
                              fontWeight: FontWeight.w500,
                              fontSize: metrics.textSize,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                ),
              ),
              SizedBox(width: metrics.buttonGap),
              _UpcomingButton(metrics: metrics, onPressed: onShowUpcoming),
            ],
          ),
        );
      },
    );
  }
}

/// Private implementation type for `_RequiredBannerMetrics` 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 _RequiredBannerMetrics {
  /// Creates a `_RequiredBannerMetrics` 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 _RequiredBannerMetrics(this.scale);

  /// Shared constant value for `referenceHeight`.
  /// The constant gives this repeated scheduler or UI value one documented source of truth instead of scattering literals across call sites.
  static const referenceHeight = 72.0;

  /// Shared constant value for `_referenceWidth`.
  /// The constant gives this repeated scheduler or UI value one documented source of truth instead of scattering literals across call sites.
  static const _referenceWidth = 760.0;

  /// Shared constant value for `_minimumScale`.
  /// The constant gives this repeated scheduler or UI value one documented source of truth instead of scattering literals across call sites.
  static const _minimumScale = 0.5;

  /// Creates a `_RequiredBannerMetrics.fromWidth` 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.
  factory _RequiredBannerMetrics.fromWidth(double width) {
    final safeWidth = width.isFinite ? width : _referenceWidth;
    final scale = (safeWidth / _referenceWidth).clamp(_minimumScale, 1.0);
    return _RequiredBannerMetrics(scale.toDouble());
  }

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

  /// Returns the derived `horizontalPadding` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get horizontalPadding => 18 * scale;

  /// Returns the derived `bannerRadius` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get bannerRadius => FocusFlowTokens.bannerRadius * scale;

  /// Returns the derived `borderWidth` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get borderWidth => scale < 0.7 ? 0.8 : 1;

  /// Returns the derived `iconCircleSize` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get iconCircleSize => 48 * scale;

  /// Returns the derived `iconSize` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get iconSize => 24 * scale;

  /// Returns the derived `iconGap` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get iconGap => 26 * scale;

  /// Returns the derived `buttonGap` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get buttonGap => 20 * scale;

  /// Returns the derived `textSize` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get textSize => (19 * scale).clamp(13.0, 14.0).toDouble();

  /// Returns the derived `buttonWidth` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get buttonWidth => 176 * scale;

  /// Returns the derived `buttonHeight` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get buttonHeight => 46 * scale;

  /// Returns the derived `buttonRadius` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get buttonRadius => FocusFlowTokens.smallButtonRadius * scale;

  /// Returns the derived `buttonTextSize` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get buttonTextSize => (16 * scale).clamp(11.0, 12.5).toDouble();

  /// Returns the derived `buttonIconSize` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get buttonIconSize => 20 * scale;

  /// Returns the derived `buttonTextGap` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  double get buttonTextGap => 6 * scale;

  /// Returns the derived `buttonPadding` value for this object.
  /// Keeping this as an accessor gives callers a stable read surface while the owning type controls how the value is calculated.
  EdgeInsets get buttonPadding => EdgeInsets.symmetric(horizontal: 12 * scale);
}

/// Private implementation type for `_UpcomingButton` 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 _UpcomingButton extends StatelessWidget {
  /// Creates a `_UpcomingButton` 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 _UpcomingButton({required this.metrics, required this.onPressed});

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

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

  /// 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 OutlinedButton(
      onPressed: onPressed,
      style: OutlinedButton.styleFrom(
        foregroundColor: FocusFlowTokens.textPrimary,
        side: BorderSide(
          color: FocusFlowTokens.navBorder.withValues(alpha: 0.55),
          width: metrics.borderWidth,
        ),
        fixedSize: Size(metrics.buttonWidth, metrics.buttonHeight),
        minimumSize: Size.zero,
        padding: metrics.buttonPadding,
        tapTargetSize: MaterialTapTargetSize.shrinkWrap,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(metrics.buttonRadius),
        ),
      ),
      child: FittedBox(
        fit: BoxFit.scaleDown,
        child: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Text(
              'Show upcoming',
              style: TextStyle(
                fontSize: metrics.buttonTextSize,
                fontWeight: FontWeight.w700,
              ),
            ),
            SizedBox(width: metrics.buttonTextGap),
            Icon(Icons.chevron_right, size: metrics.buttonIconSize),
          ],
        ),
      ),
    );
  }
}
