@using Insight.Web.Extensions

@typeparam T

@inherits ComponentBase
@implements IDisposable

@inject NavigationManager NavigationManager

<PageTitle>@Title</PageTitle>

<div class="mt-4 mb-6 ml-0">
    <div class="d-flex mb-3">
        <div>
            <MudStack Row="true">
                <MudBreadcrumbs Items="Breadcrumbs" Style="padding: 0px;" />
                @if (OnAdd.HasDelegate)
                {
                    @if (_optionalBreadcrumbs is not null)
                    {
                        <MudBreadcrumbs Items="_optionalBreadcrumbs" Class="ml-n3" Style="padding: 0px;" />
                    }

                    <MudIconButton OnClick="OnAddClick" Icon="@Icons.Material.Filled.Add" Size="Size.Small" Color="Color.Primary" Class="ml-n4" />
                }
            </MudStack>
        </div>
        <div class="ml-auto">
            <MudTextField T="string" Value="@Search" ValueChanged="@(s=>SearchAsync(s))" DebounceInterval="250" Immediate Variant="Variant.Text" Clearable OnBlur="OnSearchReleased"
                          Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" IconSize="Size.Small" Style="margin-top: -15px; width: 40vh;"/>
        </div>
    </div>
</div>

<MudTable T="T" @ref="_table" ServerData="OnLoadDataAsync" RowsPerPage="50" Loading="_loading" Elevation="0" Dense Square FixedHeader="true" FixedFooter="true" Height="calc(100vh - 205px)">
    <HeaderContent>
        @if (Header is not null)
        {
            @Header
        }
        <MudTh Style="text-align: right; width: 100px;">
            <MudStack Row="true" Class="mr-6">
                <MudButton OnClick="OnFilterClick" Variant="Variant.Text" Size="Size.Small" Color="@(Filtered ? Color.Warning : Color.Inherit)" DisableElevation Disabled="@(!OnFilter.HasDelegate)">
                    Filter
                </MudButton>
                <MudButton OnClick="RefreshAsync" Variant="Variant.Text" Size="Size.Small" DisableElevation Disabled="@(DisableRefresh || _loading)">
                    Refresh
                </MudButton>
            </MudStack>
        </MudTh>
    </HeaderContent>
    <RowTemplate>
        @if (RowTemplate is not null)
        {
            @RowTemplate(context)
        }
        <MudTd DataLabel="Actions" Style="text-align: right;">
            @if (ActionTemplate is not null)
            {
                <MudMenu Label="Actions" Variant="Variant.Text" Size="Size.Small" Dense="true" EndIcon="@Icons.Material.Filled.KeyboardArrowDown" IconColor="Color.Primary"
                     ActivationEvent="@MouseEvent.MouseOver" AnchorOrigin="Origin.BottomRight" FullWidth="true" Disabled="@(DisableAction)">
                    @if (ActionTemplate is not null)
                    {
                        @ActionTemplate(context)
                    }
                </MudMenu>
            }
        </MudTd>
    </RowTemplate>
    <PagerContent>
        <MudTablePager RowsPerPageString="" />
    </PagerContent>
</MudTable>

@code{
    [Parameter] public string Title { get; set; } = Global.Name;
    [Parameter] public List<BreadcrumbItem>? Breadcrumbs { get; set; }
    [Parameter] public Func<TableState, Task<TableData<T>>>? Data { get; set; }
    [Parameter] public RenderFragment? Header { get; set; }
    [Parameter] public RenderFragment<T>? RowTemplate { get; set; }
    [Parameter] public RenderFragment<T>? ActionTemplate { get; set; }
    [Parameter] public bool DisableAction { get; set; }
    [Parameter] public bool DisableRefresh { get; set; }
    [Parameter] public string? Search
    {
        get => _search;
        set
        {
            if (_search == value) return;
            _search = value;

            SearchChanged.InvokeAsync(value);
        }
    }
    [Parameter] public bool Filtered { get; set; }
    [Parameter] public EventCallback<string?> SearchChanged { get; set; }
    [Parameter] public EventCallback OnFilter { get; set; }
    [Parameter] public EventCallback OnAdd { get; set; }

    private List<BreadcrumbItem>? _optionalBreadcrumbs;
    private MudTable<T>? _table;
    private string? _search;
    private bool _loading;
    private bool _disposed;

    private bool Loading
    {
        get => _loading;
        set
        {
            if (value != _loading)
            {
                _loading = value;
                StateHasChanged();
            }
        }
    }

    protected override void OnInitialized()
    {
        if (OnAdd.HasDelegate)
        {
            _optionalBreadcrumbs = new List<BreadcrumbItem>()
            {
                new("", "#", true),
                new("", "#", true)
            };
        }

        if (NavigationManager.GetQueryString().TryGetValue("search", out var search))
        {
            Search = search.ToString();
        }
    }

    private async Task<TableData<T>> OnLoadDataAsync(TableState state)
    {
        if (Data is null) throw new MissingMethodException(nameof(Data));

        try
        {
            Loading = true;
            return await Data(state);
        }
        finally
        {
            Loading = false;
        }
    }

    public async Task RefreshAsync()
    {
        if (Loading || _table is null) return;

        try
        {
            Loading = true;
            await _table.ReloadServerData();
        }
        finally
        {
            Loading = false;
        }
    }

    private async Task SearchAsync(string? text)
    {
        Search = text;

        if (Loading) return;
        await RefreshAsync();
    }

    private void OnSearchReleased() => NavigationManager.ChangeQueryStringValue("search", Search);

    private async Task OnFilterClick()
    {
        if (OnFilter.HasDelegate) await OnFilter.InvokeAsync(this);
    }

    private async Task OnAddClick()
    {
        if (OnAdd.HasDelegate) await OnAdd.InvokeAsync(this);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    private void Dispose(bool disposing)
    {
        if (_disposed is false) return;
        if (disposing is false) return;

        try
        {

        }
        finally
        {
            _disposed = true;
        }
    }
}