@page "/search"
@page "/search/{SearchTerms}"

@using Microsoft.AspNetCore.Components.Web
@using Microsoft.EntityFrameworkCore
@using Microsoft.Extensions.Logging
@using MudBlazor
@using NpgsqlTypes
@using OpenArchival.DataAccess
@using Persic
@using Npgsql.EntityFrameworkCore.PostgreSQL
@using System.Linq
@using System.Linq.Expressions

@using OpenArchival.Blazor.ArtifactGroupingDisplay
@using OpenArchival.Blazor.ArchiveSearch

@namespace OpenArchival.Blazor.ArchiveDisplay

<ArchiveSearchBar SearchTermsChanged="OnSearchSubmittedAsync"/>
<FilterSelectorComponent 
    @bind-SelectedFilter="_selectedFilter" 
    @bind-SelectedTitlePrefix="_selectedLetter"
    ApplyFiltersClicked="OnApplyFilters"></FilterSelectorComponent>

@if (_totalResults > 0 && !_allArtifactsMode)
{
    <MudGrid Justify="Justify.FlexStart" Class="mt-1 ml-1 mb-2">
        <MudText Typo="Typo.subtitle2" Class="my-2">@_totalResults results found</MudText>
        <MudButton Class="ml-1"
                   StartIcon="@Icons.Material.Filled.Clear"
                   OnClick="@(args => OnClearResults(args, true, true))"
                   Variant="Variant.Filled"
                   Color="Color.Primary"
                   Size="Size.Small">Clear</MudButton>
    </MudGrid>
}

@if (_artifactGroupings.Count > 0)
{
    <MudGrid>
        @foreach (var grouping in _artifactGroupings)
        {
            <MudItem xs="12" sm="6" md="4" lg="4">
                <ArtifactGroupingSearchResult ArtifactGrouping="grouping"></ArtifactGroupingSearchResult>
            </MudItem>
        }
    </MudGrid>

    <MudPaper Class="d-flex justify-center py-2 mt-4" Elevation="0">
        <MudPagination Count="_totalPages" Selected="_currentPage" SelectedChanged="OnPageChangedAsync" />
    </MudPaper>
}


@inject IDbContextFactory<ApplicationDbContext> ContextFactory;
@inject ILogger<SearchArchive> Logger;
@inject NavigationManager NavigationManager;
@code {
    [Parameter]
    public string SearchTerms { get; set; } = "";

    private ArchiveSearchFilterType _selectedFilter = ArchiveSearchFilterType.All;

    // Field to store the current filter logic
    private Expression<Func<ArtifactGrouping, bool>> _currentFilterPredicate;

    private List<ArtifactGrouping> _artifactGroupings { get; set; } = [];
    private int _currentPage { get; set; } = 1;
    private int _totalPages { get; set; } = 0;
    private int _totalResults { get; set; } = 0;
    private const int PageSize = 20;

    private bool _allArtifactsMode = true;
    private string? _selectedLetter;

    protected override async Task OnParametersSetAsync()
    {
        if (string.IsNullOrWhiteSpace(SearchTerms))
        {
            _allArtifactsMode = true;
            await LoadPageAsync(1);
        } else
        {
            _allArtifactsMode = false;
            await PerformSearchAsync();
        }
    }

    private IQueryable<ArtifactGrouping> BuildFilterQuery(IQueryable<ArtifactGrouping> startingQuery)
    {
        // Filter
        if (_currentFilterPredicate != null && !_allArtifactsMode)
        {
            startingQuery = startingQuery.Where(_currentFilterPredicate);
        }
        if (!string.IsNullOrWhiteSpace(_selectedLetter))
        {
            string likePattern = $"{_selectedLetter}%";
            startingQuery = startingQuery.Where(artifact => EF.Functions.ILike(artifact.Title, likePattern));
        }

        return startingQuery;
    }

    /// <summary>
    /// Called by the ArchiveSearchBar component's 'SearchTermsChanged' event.
    /// </summary>
    private async Task OnSearchSubmittedAsync(string searchTerms)
    {

        await OnClearResults(null, false, true);
        if (string.IsNullOrWhiteSpace(searchTerms))
        {
            // Tell the clear operation not to navigate back to the search page and refresh
            _allArtifactsMode = true;
            await PerformSearchAsync();
            return;
        }

        SearchTerms = searchTerms;
        NavigationManager.NavigateTo($"/search/{Uri.EscapeDataString(SearchTerms)}", replace: true);
        _allArtifactsMode = false;
        // This eventually calls LoadPageAsync
        await PerformSearchAsync();
    }

    /// <summary>
    /// Sets the search predicate, calculates total results, and loads the first page.
    /// </summary>
    private async Task PerformSearchAsync()
    {
        // Determine which filter expression to use
        switch (_selectedFilter)
        {
            case ArchiveSearchFilterType.Tags:
                _currentFilterPredicate = x => x.TagsSearchVector.Matches(SearchTerms);
                break;
            case ArchiveSearchFilterType.Title:
                _currentFilterPredicate = x => x.TitleSearchVector.Matches(SearchTerms);
                break;
            case ArchiveSearchFilterType.Description:
                _currentFilterPredicate = x => x.DescriptionSearchVector.Matches(SearchTerms);
                break;
            case ArchiveSearchFilterType.Defects:
                _currentFilterPredicate = x => x.DefectsSearchVector.Matches(SearchTerms);
                break;
            case ArchiveSearchFilterType.Filenames:
                _currentFilterPredicate = x => x.FilenamesSearchVector.Matches(SearchTerms);
                break;
            case ArchiveSearchFilterType.ArtifactTranscriptions:
                _currentFilterPredicate = x => x.FileContentSearchVector.Matches(SearchTerms);
                break;
            case ArchiveSearchFilterType.ListedNames:
                _currentFilterPredicate = x => x.ListedNamesSearchVector.Matches(SearchTerms);
                break;
            case ArchiveSearchFilterType.All:
            default:
                _currentFilterPredicate = x => x.AllSearchVector.Matches(SearchTerms);
                break;
        }

        // Load the first page with the chosen filter
        await LoadPageAsync(1);
    }

    private async Task OnPageChangedAsync(int page)
    {
        await LoadPageAsync(page);
    }

    /// <summary>
    /// Fetches a specific page of data from the database using the currently set filter.
    /// </summary>
    private async Task LoadPageAsync(int page)
    {
        await using var context = await ContextFactory.CreateDbContextAsync();
        IQueryable<ArtifactGrouping> query = context.ArtifactGroupings;

        query = BuildFilterQuery(query); 

        // Select child data we want
        query = query
            .Include(x => x.ChildArtifactEntries)
            .ThenInclude(x => x.Files);

        // If we are showing all artifacts, then order results alphabetically
        if (_allArtifactsMode)
        {
            query = query.OrderBy(artifact => artifact.Title);
        } else
        {
            query = query.OrderBy(artifact => artifact.Id);
        }

        _totalResults = await query.CountAsync();
        _totalPages = (int)Math.Ceiling(_totalResults / (double)PageSize);
        _currentPage = page;

        query = query
                .Skip((_currentPage - 1) * PageSize)
                .Take(PageSize);


        _artifactGroupings = await query.ToListAsync();

        StateHasChanged();
    }

    private async Task OnClearResults(Microsoft.AspNetCore.Components.Web.MouseEventArgs args, bool navigate = true, bool resetFirstLetterFilter = false)
    {
        _totalResults = 0;
        _artifactGroupings.Clear();
        _currentPage = 1;
        _totalPages = 0;
        SearchTerms = "";

        if (navigate)
        {
            NavigationManager.NavigateTo("/search", replace: true);
        }

        if (resetFirstLetterFilter)
        {
            _selectedLetter = "";
        }

        StateHasChanged();
    }

    private async Task OnApplyFilters(MouseEventArgs args)
    {
        if (string.IsNullOrWhiteSpace(SearchTerms))
        {
            _allArtifactsMode = true;
        } else
        {
            _allArtifactsMode = false;
        }
        await PerformSearchAsync();
    }
}