@namespace Lumeo
@implements IDisposable
@inject Lumeo.Services.Localization.ILumeoLocalizer L

@if (!_isDismissed)
{
    <div role="alert" class="@CssClass" @attributes="AdditionalAttributes">
        <div class="flex gap-3">
            @if (IconContent is not null)
            {
                <div class="shrink-0 mt-0.5">
                    @IconContent
                </div>
            }
            else if (ShowIcon)
            {
                <div class="shrink-0 mt-0.5">
                    @switch (Variant)
                    {
                        case AlertVariant.Destructive:
                            <Blazicon Svg="Lucide.TriangleAlert" class="@IconSizeClass" />
                            break;
                        case AlertVariant.Success:
                            <Blazicon Svg="Lucide.CircleCheck" class="@IconSizeClass" />
                            break;
                        case AlertVariant.Warning:
                            <Blazicon Svg="Lucide.TriangleAlert" class="@IconSizeClass" />
                            break;
                        case AlertVariant.Info:
                            <Blazicon Svg="Lucide.Info" class="@IconSizeClass" />
                            break;
                        default:
                            <Blazicon Svg="Lucide.Info" class="@IconSizeClass" />
                            break;
                    }
                </div>
            }
            <div class="flex-1 min-w-0">
                @if (!string.IsNullOrEmpty(Title))
                {
                    <h5 class="@TitleClass">@Title</h5>
                }
                @if (!string.IsNullOrEmpty(Description))
                {
                    <div class="@DescriptionClass">@Description</div>
                }
                @ChildContent
            </div>
            @if (IsDismissible)
            {
                <div class="shrink-0">
                    <button type="button" class="rounded-md hover:bg-foreground/10 focus:outline-none focus:ring-1 focus:ring-ring p-0.5" @onclick="Dismiss" aria-label="@L["Alert.Dismiss"]">
                        <Blazicon Svg="Lucide.X" class="@IconSizeClass" />
                    </button>
                </div>
            }
        </div>
    </div>
}

@code {
    [Parameter] public RenderFragment? ChildContent { get; set; }
    [Parameter] public AlertVariant Variant { get; set; } = AlertVariant.Default;
    [Parameter] public AlertSize Size { get; set; } = AlertSize.Medium;
    [Parameter] public bool IsDismissible { get; set; }
    [Parameter] public EventCallback OnDismiss { get; set; }
    [Parameter] public string? Title { get; set; }
    [Parameter] public string? Description { get; set; }
    /// <summary>Icon slot. Named <c>IconContent</c> to avoid shadowing the <c>&lt;Icon&gt;</c> component.</summary>
    [Parameter] public RenderFragment? IconContent { get; set; }

    [Parameter] public bool ShowIcon { get; set; } = true;
    [Parameter] public int? AutoDismissMs { get; set; }
    [Parameter] public string? Class { get; set; }
    [Parameter(CaptureUnmatchedValues = true)] public Dictionary<string, object>? AdditionalAttributes { get; set; }

    private bool _isDismissed;
    private volatile bool _disposed;
    private CancellationTokenSource? _autoDismissCts;

    protected override void OnInitialized()
    {
        if (AutoDismissMs.HasValue && AutoDismissMs.Value > 0)
        {
            _autoDismissCts = new CancellationTokenSource();
            var timeout = AutoDismissMs.Value;
            var token = _autoDismissCts.Token;
            // Dispatch via SafeAsyncDispatcher so any outer-task fault (e.g. circuit
            // already disposed) is caught and never surfaces as an unobserved task.
            SafeAsyncDispatcher.FireAndForget(
                fn => InvokeAsync(fn),
                () => RunAutoDismissAsync(timeout, token),
                "Lumeo.Alert");
        }
    }

    private async Task RunAutoDismissAsync(int timeout, CancellationToken token)
    {
        try
        {
            await Task.Delay(timeout, token).ConfigureAwait(true);
        }
        catch (TaskCanceledException) { return; }
        catch (OperationCanceledException) { return; }

        if (_disposed || token.IsCancellationRequested) return;
        if (_isDismissed) return;
        _isDismissed = true;
        await OnDismiss.InvokeAsync();
        if (_disposed) return;
        StateHasChanged();
    }

    // Border baseline kept at /60 so the unfilled (Default variant) alert still reads as an outlined callout;
    // colored variants knock it softer since their tinted background already carries the visual weight.
    private string CssClass => $"relative w-full rounded-lg border border-border/60 {SizePaddingClass} text-sm {VariantClasses} {Class}".Trim();

    private string SizePaddingClass => Size switch
    {
        AlertSize.Small => "px-3 py-2",
        AlertSize.Large => "px-5 py-4",
        _ => "px-4 py-3"
    };

    private string IconSizeClass => Size switch
    {
        AlertSize.Small => "h-3.5 w-3.5",
        AlertSize.Large => "h-5 w-5",
        _ => "h-4 w-4"
    };

    private string TitleClass => Size switch
    {
        AlertSize.Small => "mb-0.5 text-xs font-medium leading-none tracking-tight",
        AlertSize.Large => "mb-1.5 text-base font-medium leading-none tracking-tight",
        _ => "mb-1 font-medium leading-none tracking-tight"
    };

    private string DescriptionClass => Size switch
    {
        AlertSize.Small => "text-xs opacity-90",
        AlertSize.Large => "text-sm opacity-90",
        _ => "text-sm opacity-90"
    };

    private string VariantClasses => Variant switch
    {
        AlertVariant.Default => "bg-background text-foreground",
        AlertVariant.Destructive => "border-destructive/50 text-destructive",
        AlertVariant.Success => "border-success/50 bg-success-light text-success-text",
        AlertVariant.Warning => "border-warning/50 bg-warning-light text-warning-text",
        AlertVariant.Info => "border-primary/50 bg-primary/10 text-primary",
        _ => ""
    };

    private async Task Dismiss()
    {
        if (_isDismissed) return;
        try { _autoDismissCts?.Cancel(); } catch (ObjectDisposedException) { }
        _isDismissed = true;
        await OnDismiss.InvokeAsync();
    }

    public void Dispose()
    {
        _disposed = true;
        try { _autoDismissCts?.Cancel(); } catch (ObjectDisposedException) { }
        _autoDismissCts?.Dispose();
        _autoDismissCts = null;
    }

    public enum AlertVariant { Default, Destructive, Success, Warning, Info }
    public enum AlertSize { Small, Medium, Large }
}
