using JSMR.Application.Common.Search;
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Routing;
using Microsoft.JSInterop;

namespace JSMR.UI.Blazor.Shared;

public abstract class SearchPageBase<TState, TItem> : ComponentBase, IAsyncDisposable
    where TState : notnull
{
    [Inject]
    protected NavigationManager Nav { get; set; } = default!;

    [Inject]
    protected IJSRuntime JS { get; set; } = default!;

    protected TState State { get; private set; } = default!;
    protected SearchResult<TItem>? Result { get; private set; }
    protected bool IsLoading { get; private set; }

    private bool _suppressNextLocation;
    private bool _isAlive = true;
    private CancellationTokenSource _cancellationTokenSource = new();

    protected override async Task OnInitializedAsync()
    {
        Nav.LocationChanged += OnLocationChanged;
        State = ParseStateFromUri(Nav.Uri);

        await RunSearchAsync();
    }

    protected async Task UpdateAsync(TState next)
    {
        if (Equals(next, State))
            return;

        State = next;
        NavigateToState(next);

        _ = RunSearchAsync();
    }

    //protected async Task Update2Async(TState next, bool scrollToTop)
    //{
    //    if (Equals(next, State))
    //        return;

    //    Console.WriteLine("Got here");

    //    State = next;
    //    NavigateToState(next);

    //    _ = RunSearchAsync(scrollToTop);
    //}

    private void NavigateToState(TState next)
    {
        string uri = BuildUri(next);
        _suppressNextLocation = true;
        Nav.NavigateTo(uri);
    }

    private async void OnLocationChanged(object? sender, LocationChangedEventArgs e)
    {
        if (_suppressNextLocation)
        {
            _suppressNextLocation = false;
            return;
        }

        if (!_isAlive)
            return;

        if (!IsThisPage(e.Location))
            return;

        TState next = ParseStateFromUri(e.Location);

        if (Equals(next, State))
            return;
        
        State = next;
        
        await InvokeAsync(StateHasChanged);
        _ = RunSearchAsync();
    }

    protected async Task RunSearchAsync(bool scrollTotop = true)
    {
        if (scrollTotop)
            await JS.InvokeVoidAsync("pageHelpers.scrollToTop");

        Console.WriteLine("Got here 2");

        try
        {
            IsLoading = true;
            StateHasChanged();

            _cancellationTokenSource.Cancel();
            _cancellationTokenSource = new();

            Result = await ExecuteSearchAsync(State, _cancellationTokenSource.Token);
        }
        catch (OperationCanceledException)
        {

        }
        finally
        {
            if (_isAlive)
            {
                IsLoading = false;
                await InvokeAsync(StateHasChanged);
            }
        }
    }

    protected abstract TState ParseStateFromUri(string absoluteUri);
    protected abstract string BuildUri(TState state);
    protected abstract bool IsThisPage(string absoluteUri);
    protected abstract Task<SearchResult<TItem>> ExecuteSearchAsync(TState state, CancellationToken cancellationToken);

    public async ValueTask DisposeAsync()
    {
        _isAlive = false;

        Nav.LocationChanged -= OnLocationChanged;

        _cancellationTokenSource.Cancel();
        _cancellationTokenSource.Dispose();

        GC.SuppressFinalize(this);

        await Task.CompletedTask;
    }
}