@namespace Lumeo

@if (IsIndeterminate && Shape == ProgressShape.Linear)
{
    <style>
        @@keyframes progress-indeterminate {
            0% { left: -35%; right: 100%; }
            60% { left: 100%; right: -90%; }
            100% { left: 100%; right: -90%; }
        }
        .animate-progress-indeterminate {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 30%;
            animation: progress-indeterminate 1.5s ease-in-out infinite;
        }
    </style>
}

@if (IsIndeterminate && Shape == ProgressShape.Circular)
{
    <style>
        @@keyframes circular-indeterminate {
            0% { transform: rotate(0deg); }
            100% { transform: rotate(360deg); }
        }
        .animate-circular-indeterminate {
            animation: circular-indeterminate 1.5s linear infinite;
            transform-origin: center;
        }
    </style>
}

@if ((Striped || Animated) && Shape == ProgressShape.Linear)
{
    <style>
        @@keyframes progress-stripes {
            0% { background-position: 1rem 0; }
            100% { background-position: 0 0; }
        }
        .progress-striped {
            background-image: linear-gradient(
                45deg,
                var(--color-progress-stripe) 25%,
                transparent 25%,
                transparent 50%,
                var(--color-progress-stripe) 50%,
                var(--color-progress-stripe) 75%,
                transparent 75%,
                transparent
            );
            background-size: 1rem 1rem;
        }
        .progress-animated {
            animation: progress-stripes 1s linear infinite;
        }
    </style>
}

@if (Shape == ProgressShape.Circular)
{
    @if (ShowLabel)
    {
        <div class="flex items-center gap-2">
            <div class="@CircularWrapperClass" role="progressbar" aria-valuenow="@(IsIndeterminate ? null : Value)" aria-valuemin="0" aria-valuemax="@Max" @attributes="AdditionalAttributes">
                <svg viewBox="0 0 36 36" class="@CircularSvgClass" style="@CircularSizeStyle">
                    <circle cx="18" cy="18" r="@CircularRadius" fill="none" stroke="currentColor" class="text-primary/20" stroke-width="@StrokeWidth" />
                    <circle cx="18" cy="18" r="@CircularRadius" fill="none" stroke="currentColor" class="@CircularStrokeColor"
                        stroke-width="@StrokeWidth"
                        stroke-dasharray="@Circumference.ToString(System.Globalization.CultureInfo.InvariantCulture)"
                        stroke-dashoffset="@DashOffset.ToString(System.Globalization.CultureInfo.InvariantCulture)"
                        stroke-linecap="round"
                        transform="rotate(-90 18 18)"
                        style="transition: stroke-dashoffset 0.3s ease" />
                </svg>
                @if (ShowValue)
                {
                    <span class="absolute inset-0 flex items-center justify-center text-foreground @CircularTextSize">@(Label ?? $"{Percentage}%")</span>
                }
            </div>
            @if (!ShowValue)
            {
                <span class="text-sm text-muted-foreground whitespace-nowrap">@(Label ?? $"{Percentage}%")</span>
            }
        </div>
    }
    else
    {
        <div class="@CircularWrapperClass" role="progressbar" aria-valuenow="@(IsIndeterminate ? null : Value)" aria-valuemin="0" aria-valuemax="@Max" @attributes="AdditionalAttributes">
            <svg viewBox="0 0 36 36" class="@CircularSvgClass" style="@CircularSizeStyle">
                <circle cx="18" cy="18" r="@CircularRadius" fill="none" stroke="currentColor" class="text-primary/20" stroke-width="@StrokeWidth" />
                <circle cx="18" cy="18" r="@CircularRadius" fill="none" stroke="currentColor" class="@CircularStrokeColor"
                    stroke-width="@StrokeWidth"
                    stroke-dasharray="@Circumference.ToString(System.Globalization.CultureInfo.InvariantCulture)"
                    stroke-dashoffset="@DashOffset.ToString(System.Globalization.CultureInfo.InvariantCulture)"
                    stroke-linecap="round"
                    transform="rotate(-90 18 18)"
                    style="transition: stroke-dashoffset 0.3s ease" />
            </svg>
            @if (ShowValue)
            {
                <span class="absolute inset-0 flex items-center justify-center text-foreground @CircularTextSize">@(Label ?? $"{Percentage}%")</span>
            }
        </div>
    }
}
else
{
    @if (ShowLabel)
    {
        <div class="flex items-center gap-2">
            <div class="@CssClass" role="progressbar" aria-valuenow="@(IsIndeterminate ? null : Value)" aria-valuemin="0" aria-valuemax="@Max" @attributes="AdditionalAttributes">
                @if (IsIndeterminate)
                {
                    <div class="@($"animate-progress-indeterminate rounded-[var(--radius)] {VariantClasses}")"></div>
                }
                else
                {
                    <div class="@IndicatorClasses" style="@IndicatorStyle"></div>
                }
            </div>
            <span class="text-sm text-muted-foreground whitespace-nowrap">@(Label ?? $"{Percentage}%")</span>
        </div>
    }
    else
    {
        <div class="@CssClass" role="progressbar" aria-valuenow="@(IsIndeterminate ? null : Value)" aria-valuemin="0" aria-valuemax="@Max" @attributes="AdditionalAttributes">
            @if (IsIndeterminate)
            {
                <div class="@($"animate-progress-indeterminate rounded-[var(--radius)] {VariantClasses}")"></div>
            }
            else
            {
                <div class="@IndicatorClasses" style="@IndicatorStyle"></div>
            }
        </div>
    }
}

@code {
    [Parameter] public int Value { get; set; }
    [Parameter] public int Max { get; set; } = 100;
    [Parameter] public ProgressVariant Variant { get; set; } = ProgressVariant.Default;
    [Parameter] public ProgressShape Shape { get; set; } = ProgressShape.Linear;
    [Parameter] public bool IsIndeterminate { get; set; }
    [Parameter] public bool ShowLabel { get; set; }
    [Parameter] public bool ShowValue { get; set; }
    [Parameter] public string? Label { get; set; }
    [Parameter] public bool Striped { get; set; }
    [Parameter] public bool Animated { get; set; }

    /// <summary>
    /// Opt-in animation variant for the linear progress fill. <c>None</c> (default)
    /// leaves existing behaviour untouched. <c>Stripe</c> overlays translucent
    /// diagonal stripes that sweep across the bar.
    /// </summary>
    [Parameter] public ProgressAnimation Animation { get; set; } = ProgressAnimation.None;
    [Parameter] public string? GradientFrom { get; set; }
    [Parameter] public string? GradientTo { get; set; }
    [Parameter] public string Size { get; set; } = "Default";
    [Parameter] public int StrokeWidth { get; set; } = 4;
    [Parameter] public string? Class { get; set; }
    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }

    private int Percentage => Max > 0 ? Math.Min(100, (int)((double)Value / Max * 100)) : 0;

    // --- Linear progress ---
    private string SizeClass => Size switch
    {
        "Small" => "h-1",
        "Large" => "h-4",
        _ => "h-2"
    };

    private string CssClass => string.Join(" ", new[] { "relative", SizeClass, "w-full overflow-hidden rounded-[var(--radius)] bg-primary/20", Class }.Where(s => !string.IsNullOrWhiteSpace(s)));

    private string IndicatorClasses
    {
        get
        {
            var stripeClasses = Striped || Animated ? "progress-striped" : null;
            var animClasses = Animated ? "progress-animated" : null;
            var enumStripeClass = Animation == ProgressAnimation.Stripe ? "lumeo-progress-stripes" : null;
            var enumIndeterminateClass = Animation == ProgressAnimation.Indeterminate ? "lumeo-progress-indeterminate" : null;
            var enumGlowClass = Animation == ProgressAnimation.Glow ? "lumeo-progress-glow" : null;
            // Indeterminate sweep uses currentColor for its ::before pseudo — swap bg-* → text-* so the variant color feeds through.
            var colorClasses = (GradientFrom is not null && GradientTo is not null)
                ? null
                : (Animation == ProgressAnimation.Indeterminate ? IndeterminateColorClasses : VariantClasses);
            return string.Join(" ", new[] { "h-full rounded-[var(--radius)] transition-all duration-300", colorClasses, stripeClasses, animClasses, enumStripeClass, enumIndeterminateClass, enumGlowClass }.Where(s => !string.IsNullOrWhiteSpace(s)));
        }
    }

    private string IndeterminateColorClasses => Variant switch
    {
        ProgressVariant.Default => "text-primary",
        ProgressVariant.Success => "text-success",
        ProgressVariant.Warning => "text-warning",
        ProgressVariant.Destructive => "text-destructive",
        _ => "text-primary"
    };

    private string IndicatorStyle
    {
        get
        {
            // `Indeterminate` animation hides the fill and lets a narrow
            // pseudo-element slide across the track — so force 100% width and
            // fully transparent background so only the ::before sweep shows.
            if (Animation == ProgressAnimation.Indeterminate)
            {
                return "width: 100%; background: transparent";
            }
            var width = $"width: {Percentage}%";
            if (GradientFrom is not null && GradientTo is not null)
            {
                return $"{width}; background: linear-gradient(90deg, {GradientFrom}, {GradientTo})";
            }
            return width;
        }
    }

    private string VariantClasses => Variant switch
    {
        ProgressVariant.Default => "bg-primary",
        ProgressVariant.Success => "bg-success",
        ProgressVariant.Warning => "bg-warning",
        ProgressVariant.Destructive => "bg-destructive",
        _ => "bg-primary"
    };

    // --- Circular progress ---
    private const double CircularRadius = 16;
    private double Circumference => 2 * Math.PI * CircularRadius;
    private double DashOffset => IsIndeterminate ? Circumference * 0.75 : Circumference * (1.0 - Percentage / 100.0);

    private string CircularSizeStyle => Size switch
    {
        "Small" => "width: 40px; height: 40px",
        "Large" => "width: 80px; height: 80px",
        _ => "width: 56px; height: 56px"
    };

    private string CircularTextSize => Size switch
    {
        "Small" => "text-[10px] font-medium",
        "Large" => "text-sm font-semibold",
        _ => "text-xs font-medium"
    };

    private string CircularWrapperClass => string.Join(" ", new[] { "relative inline-flex items-center justify-center", Class }.Where(s => !string.IsNullOrWhiteSpace(s)));

    private string CircularSvgClass => IsIndeterminate ? "animate-circular-indeterminate" : "";

    private string CircularStrokeColor => Variant switch
    {
        ProgressVariant.Default => "text-primary",
        ProgressVariant.Success => "text-success",
        ProgressVariant.Warning => "text-warning",
        ProgressVariant.Destructive => "text-destructive",
        _ => "text-primary"
    };

    public enum ProgressVariant { Default, Success, Warning, Destructive }
    public enum ProgressShape { Linear, Circular }

    /// <summary>Animation variants for linear progress.</summary>
    /// <remarks>
    /// <list type="bullet">
    /// <item><c>None</c> — no animation (default).</item>
    /// <item><c>Stripe</c> — translucent diagonal stripes sweep across the fill.</item>
    /// <item><c>Indeterminate</c> — a narrow bright segment slides continuously.
    /// The bound <c>Value</c> is visually ignored while this plays.</item>
    /// <item><c>Glow</c> — the fill bar's box-shadow breathes, suggesting activity.</item>
    /// </list>
    /// </remarks>
    public enum ProgressAnimation { None, Stripe, Indeterminate, Glow }
}
