@namespace SufiChain.SufiBlazor.Components.Overlays
@implements IAsyncDisposable
@inject IJSRuntime JSRuntime
@inject IStringLocalizer<SufiBlazorResource> L

<dialog @ref="_dialogRef"
        class="@CssClass"
        style="@EffectiveStyle"
        @onkeydown="HandleKeyDown"
        @oncancel="HandleCancel"
        @oncancel:preventDefault="true"
        @onclose="HandleNativeClose"
        aria-labelledby="@(Title != null ? _titleId : null)"
        aria-modal="true"
        @attributes="FilteredAttributes">
    <div class="sb-dialog__container" @onclick:stopPropagation="true">
        @if (ShowHeader)
        {
            <header class="sb-dialog__header">
                @if (Header != null)
                {
                    @Header
                }
                else if (Title != null)
                {
                    <h2 id="@_titleId" class="sb-dialog__title">@Title</h2>
                }
                @if (ShowCloseButton)
                {
                    <button type="button"
                            class="sb-dialog__close-btn"
                            @onclick="HandleCloseButtonClick"
                            aria-label="@L["Close"]">
                        <span aria-hidden="true">×</span>
                    </button>
                }
            </header>
        }
        <div class="sb-dialog__body">
            @ChildContent
        </div>
        @if (Footer != null)
        {
            <footer class="sb-dialog__footer">
                @Footer
            </footer>
        }
    </div>
</dialog>

@code {
    private ElementReference _dialogRef;
    private readonly string _titleId = $"sb-dialog-title-{Guid.NewGuid():N}";
    private bool _isOpen;
    private bool _hasRendered;
    private bool _pendingOpenSync;
    private bool _suppressNextNativeClose;

    /// <summary>
    /// Whether the dialog is open.
    /// </summary>
    [Parameter]
    public bool Open { get; set; }

    /// <summary>
    /// Callback when open state changes.
    /// </summary>
    [Parameter]
    public EventCallback<bool> OpenChanged { get; set; }

    /// <summary>
    /// Dialog title displayed in header.
    /// </summary>
    [Parameter]
    public string? Title { get; set; }

    /// <summary>
    /// Custom header content (replaces Title).
    /// </summary>
    [Parameter]
    public RenderFragment? Header { get; set; }

    /// <summary>
    /// Footer content for actions.
    /// </summary>
    [Parameter]
    public RenderFragment? Footer { get; set; }

    /// <summary>
    /// Dialog body content.
    /// </summary>
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    /// <summary>
    /// Dialog size preset (e.g. Lg). Used when Width/MaxWidth/Height/MaxHeight are not set; with Size alone the modal uses the preset dimensions.
    /// </summary>
    [Parameter]
    public SbDialogSize Size { get; set; } = SbDialogSize.Md;

    /// <summary>
    /// Optional. Override width. Value is applied as-is to CSS (e.g. "800px", "80%", "50vw"). When set, overrides Size for width.
    /// </summary>
    [Parameter]
    public string? Width { get; set; }

    /// <summary>
    /// Optional. Override max-width. Value is applied as-is to CSS (e.g. "800px", "80%", "1200px"). When set, overrides Size for max-width.
    /// </summary>
    [Parameter]
    public string? MaxWidth { get; set; }

    /// <summary>
    /// Optional. Override height. Value is applied as-is to CSS (e.g. "80vh", "600px", "80%").
    /// </summary>
    [Parameter]
    public string? Height { get; set; }

    /// <summary>
    /// Optional. Override max-height. Value is applied as-is to CSS (e.g. "90vh", "800px", "80%").
    /// </summary>
    [Parameter]
    public string? MaxHeight { get; set; }

    /// <summary>
    /// Whether to close on Escape key.
    /// </summary>
    [Parameter]
    public bool CloseOnEscape { get; set; } = true;

    /// <summary>
    /// Whether to close when clicking backdrop. Default is false so backdrop clicks do nothing.
    /// </summary>
    [Parameter]
    public bool CloseOnBackdropClick { get; set; } = false;

    /// <summary>
    /// Whether to show the close button.
    /// </summary>
    [Parameter]
    public bool ShowCloseButton { get; set; } = true;

    /// <summary>
    /// Optional callback when the header close (X) button is clicked.
    /// Runs before the standard close flow; the dialog still invokes OpenChanged(false).
    /// </summary>
    [Parameter]
    public EventCallback OnCloseButtonClick { get; set; }

    /// <summary>
    /// Callback when dialog closes with reason.
    /// </summary>
    [Parameter]
    public EventCallback<SbDialogCloseReason> OnClose { get; set; }

    /// <summary>
    /// When true, the dialog body uses overflow: visible so dropdowns (e.g. SbSelect) inside the modal
    /// are not clipped by the body. Use for modals that contain selects or other overflow menus.
    /// </summary>
    [Parameter]
    public bool AllowDropdownOverflow { get; set; }

    /// <summary>
    /// Additional CSS classes.
    /// </summary>
    [Parameter]
    public string? Class { get; set; }

    /// <summary>
    /// Inline styles.
    /// </summary>
    [Parameter]
    public string? Style { get; set; }

    /// <summary>
    /// Additional HTML attributes.
    /// </summary>
    [Parameter(CaptureUnmatchedValues = true)]
    public Dictionary<string, object>? AdditionalAttributes { get; set; }

    private bool ShowHeader => Header != null || Title != null || ShowCloseButton;

    private static string? GetDimensionValue(string? parameterValue, Dictionary<string, object>? attrs, params string[] keys)
    {
        if (!string.IsNullOrWhiteSpace(parameterValue))
            return parameterValue.Trim();
        if (attrs == null)
            return null;
        foreach (var key in keys)
        {
            if (attrs.TryGetValue(key, out var v) && v is string s && !string.IsNullOrWhiteSpace(s))
                return s.Trim();
        }
        return null;
    }

    private bool HasDimensionOverrides
    {
        get
        {
            var w = GetDimensionValue(Width, AdditionalAttributes, "Width", "width");
            var mw = GetDimensionValue(MaxWidth, AdditionalAttributes, "MaxWidth", "max-width", "maxwidth");
            var h = GetDimensionValue(Height, AdditionalAttributes, "Height", "height");
            var mh = GetDimensionValue(MaxHeight, AdditionalAttributes, "MaxHeight", "max-height", "maxheight");
            return w != null || mw != null || h != null || mh != null;
        }
    }

    private IReadOnlyDictionary<string, object>? FilteredAttributes
    {
        get
        {
            if (AdditionalAttributes == null || AdditionalAttributes.Count == 0)
                return AdditionalAttributes;
            var omit = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
                { "width", "maxwidth", "max-width", "height", "maxheight", "max-height" };
            var filtered = new Dictionary<string, object>(AdditionalAttributes);
            foreach (var key in AdditionalAttributes.Keys)
            {
                if (omit.Contains(key))
                    filtered.Remove(key);
            }
            return filtered.Count == AdditionalAttributes.Count ? AdditionalAttributes : filtered;
        }
    }

    private string EffectiveStyle
    {
        get
        {
            var parts = new List<string>();
            var w = GetDimensionValue(Width, AdditionalAttributes, "Width", "width");
            if (w != null)
                parts.Add($"width: {w}");
            var mw = GetDimensionValue(MaxWidth, AdditionalAttributes, "MaxWidth", "max-width", "maxwidth");
            if (mw != null)
                parts.Add($"max-width: {mw}");
            var h = GetDimensionValue(Height, AdditionalAttributes, "Height", "height");
            if (h != null)
                parts.Add($"height: {h}");
            var mh = GetDimensionValue(MaxHeight, AdditionalAttributes, "MaxHeight", "max-height", "maxheight");
            if (mh != null)
                parts.Add($"max-height: {mh}");
            if (!string.IsNullOrWhiteSpace(Style))
                parts.Add(Style.Trim());
            return parts.Count > 0 ? string.Join("; ", parts) : string.Empty;
        }
    }

    private string CssClass
    {
        get
        {
            var classes = new List<string> { "sb-dialog" };
            if (!HasDimensionOverrides)
                classes.Add($"sb-dialog--{Size.ToString().ToLowerInvariant()}");
            if (AllowDropdownOverflow)
                classes.Add("sb-dialog--allow-dropdown-overflow");
            if (!string.IsNullOrWhiteSpace(Class))
                classes.Add(Class);
            return string.Join(" ", classes);
        }
    }

    protected override async Task OnParametersSetAsync()
    {
        if (Open != _isOpen)
        {
            _isOpen = Open;
            await SyncNativeOpenStateAsync();
        }
    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        _hasRendered = true;

        if (_pendingOpenSync)
        {
            await SyncNativeOpenStateAsync();
        }
    }

    private async Task ShowAsync()
    {
        await JSRuntime.InvokeVoidAsync("SufiBlazor.dialog.showModal", _dialogRef);
    }

    private async Task HideAsync()
    {
        await JSRuntime.InvokeVoidAsync("SufiBlazor.dialog.close", _dialogRef);
    }

    private async Task SyncNativeOpenStateAsync()
    {
        if (!_hasRendered)
        {
            _pendingOpenSync = true;
            return;
        }

        _pendingOpenSync = false;

        if (_isOpen)
        {
            await ShowAsync();
        }
        else
        {
            await HideAsync();
        }
    }

    private async Task HandleCloseButtonClick()
    {
        if (OnCloseButtonClick.HasDelegate)
        {
            await OnCloseButtonClick.InvokeAsync();
        }

        await CloseAsync(SbDialogCloseReason.CloseButton);
    }

    private async Task CloseAsync(SbDialogCloseReason reason)
    {
        _suppressNextNativeClose = true;
        _isOpen = false;
        await SyncNativeOpenStateAsync();
        await OnClose.InvokeAsync(reason);
        await OpenChanged.InvokeAsync(false);
    }

    private async Task HandleKeyDown(KeyboardEventArgs args)
    {
        if (args.Key == "Escape" && CloseOnEscape)
        {
            await CloseAsync(SbDialogCloseReason.Escape);
        }
    }

    private async Task HandleCancel()
    {
        // Native dialog cancel event (Escape key)
        if (CloseOnEscape)
        {
            await CloseAsync(SbDialogCloseReason.Escape);
        }
    }

    private async Task HandleNativeClose()
    {
        if (_suppressNextNativeClose)
        {
            _suppressNextNativeClose = false;
            return;
        }

        if (!_isOpen)
        {
            return;
        }

        _isOpen = false;
        await OnClose.InvokeAsync(SbDialogCloseReason.CloseButton);
        await OpenChanged.InvokeAsync(false);
    }

    public async ValueTask DisposeAsync()
    {
        // Cleanup if needed
    }
}
