@using Microsoft.JSInterop
@using System.Linq
@using Radzen.Blazor
@using Radzen.Blazor.Rendering
@inject IJSRuntime JSRuntime
@implements IAsyncDisposable

    <div @ref=@element class="@($"rz-event {CssClass}".Trim())" style=@Style
         role="button" tabindex="0" aria-label="@Data?.Text"
         @onclick=@OnClick @onkeydown="OnKeyDown" @onmouseenter=@OnMouseEnter @onmouseleave=@OnMouseLeave
         @attributes="@FilteredAdditionalAttributes">
        <div class="rz-event-content" title=@Title @attributes=@Attributes>
            @if (ChildContent != null)
            {
                @ChildContent
            }
            else if (ShowAppointmentContent && Scheduler != null && Data != null)
            {
                @Scheduler.RenderAppointment(Data)
            }
        </div>
    </div>
@code {
    private ElementReference element;

    private string? Title => Scheduler?.HasMouseEnterAppointmentDelegate() == true ? null : Data?.Text;

    [Parameter]
    public string? CssClass { get; set; }

    [Parameter]
    public double? Top { get; set; }

    [Parameter]
    public double? Left { get; set; }

    [Parameter]
    public double? Width { get; set; }

    [Parameter]
    public double? Height { get; set; }

    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    [Parameter]
    public EventCallback<AppointmentData> Click { get; set; }

    [Parameter]
    public EventCallback<AppointmentData> DragStart { get; set; }

    IDictionary<string, object> Attributes { get; set; } = new Dictionary<string, object>();

    [Parameter]
    public AppointmentData? Data { get; set; }

    [Parameter]
    public bool ShowAppointmentContent { get; set; } = true;

    /// <summary>
    /// Extra HTML attributes rendered on the outer element.
    /// </summary>
    [Parameter]
    public IReadOnlyDictionary<string, object>? AdditionalAttributes { get; set; }

    private IReadOnlyDictionary<string, object>? FilteredAdditionalAttributes =>
        AdditionalAttributes != null && AdditionalAttributes.ContainsKey("style")
            ? AdditionalAttributes.Where(kv => kv.Key != "style").ToDictionary(kv => kv.Key, kv => kv.Value)
            : AdditionalAttributes;

    [CascadingParameter]
    public IScheduler? Scheduler { get; set; }

    string Style { get; set; } = string.Empty;

    protected override void OnParametersSet()
    {
        Attributes = (Scheduler != null && Data != null) ? Scheduler.GetAppointmentAttributes(Data) : new Dictionary<string, object>();

        var style = new List<string>();

        if (Top.HasValue)
        {
            style.Add($"inset-block-start: {Top.ToInvariantString()}em");
        }

        if (Left.HasValue)
        {
            style.Add($"inset-inline-start: {Left.ToInvariantString()}%");
        }

        if (Width.HasValue)
        {
            style.Add($"width: {Width.ToInvariantString()}%");
        }

        if (Height.HasValue)
        {
            style.Add($"height: {Height.ToInvariantString()}em");
        }

        Style = String.Join(";", style);

        if (AdditionalAttributes != null && AdditionalAttributes.TryGetValue("style", out var extraStyle) && extraStyle is string s && !string.IsNullOrWhiteSpace(s))
        {
            Style = string.IsNullOrWhiteSpace(Style) ? s : Style.TrimEnd(';') + ";" + s;
        }
    }

    async Task OnClick()
    {
        await Click.InvokeAsync(Data);
    }

    async Task OnKeyDown(KeyboardEventArgs args)
    {
        var key = args.Code != null ? args.Code : args.Key;
        if (key == "Enter" || key == "Space")
        {
            await OnClick();
        }
    }

    async Task OnMouseEnter(MouseEventArgs e)
    {
        if (Scheduler != null && Data != null)
        {
            await Scheduler.MouseEnterAppointment(element, Data, e.ClientX, e.ClientY);
        }
    }

    async Task OnMouseLeave(MouseEventArgs e)
    {
        if (Scheduler != null && Data != null)
        {
            await Scheduler.MouseLeaveAppointment(element, Data, e.ClientX, e.ClientY);
        }
    }

    [JSInvokable]
    public async Task OnDragStartAsync()
    {
        await DragStart.InvokeAsync(Data);
    }

    private DotNetObjectReference<Appointment>? dotnetRef;

    private IJSObjectReference? jsRef;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender && Scheduler?.HasAppointmentMoveDelegate() == true)
        {
            dotnetRef = DotNetObjectReference.Create(this);
            jsRef = await JSRuntime.InvokeAsync<IJSObjectReference>("Radzen.createDraggable", element, dotnetRef, nameof(OnDragStartAsync));
        }
    }


    public async ValueTask DisposeAsync()
    {
        try
        {
            if (jsRef != null)
            {
                await jsRef.InvokeVoidAsync("dispose");
                await jsRef.DisposeAsync();
            }
        }
        catch (JSDisconnectedException)
        {
        }
        finally
        {
            dotnetRef?.Dispose();
        }

    }
}