@using Iceshrimp.Shared.Schemas.Web
@using Ljbc1994.Blazor.IntersectionObserver
@using Ljbc1994.Blazor.IntersectionObserver.API
@using Ljbc1994.Blazor.IntersectionObserver.Components
@inject IJSRuntime Js
@if (_init) // FIXME: We need to wait for the Component to render once before initializing the Intersection Observer.
    // With the <IntersectionObserver> Component this is AFAIK only possible by not rendering it until then.
    // The proper fix for this is to change to the Service Pattern.
    // But that requires the IntersectionObserver Library to be modified to return what element an observation update is for.
{
    <IntersectionObserve Options="new IntersectionObserverOptions { Root = Scroller, RootMargin = Margin }" OnChange="@(entry => Change(entry))">
        <div class="tgt" @ref="context.Ref.Current">
            @if (_isIntersecting)
            {
                <TimelineNote Note="Note"/>
            }
            else
            {
                <div class="placeholder" style="height: @(Height ?? 150)px"></div>
            }
        </div>
    </IntersectionObserve>
}

@code {
    [Parameter] [EditorRequired] public required NoteResponse     Note     { get; set; }
    [Parameter] [EditorRequired] public          ElementReference Scroller { get; set; }

    private const string              Margin = "200%";
    private       IJSObjectReference? _module;
    private       int?                Height { get; set; }
    private       bool                _isIntersecting = true;
    private       bool                _init;

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            _module = await Js.InvokeAsync<IJSObjectReference>("import",
                                                               "./Components/LazyNote.razor.js");
        }
    }

    protected override void OnAfterRender(bool firstRender)
    {
        if (firstRender)
        {
            _init = true;
        }
    }

    private async Task Change(IntersectionObserverEntry entry)
    {
        if (entry.IsIntersecting == false)
        {
            Height          = await GetHeight();
            _isIntersecting = false;
            StateHasChanged();
            return;
        }

        if (entry.IsIntersecting)
        {
            _isIntersecting = true;
        }
    }

    private async Task<int?> GetHeight()
    {
        var height = await (_module ?? throw new Exception("Call to JS module was made before it was loaded, this should not have happened"))
            .InvokeAsync<int?>("getScrollHeight", Note.Id);
        return height;
    }
}