using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.Extensions.Logging;
using MudBlazor;
using Npgsql.EntityFrameworkCore.PostgreSQL;
using NpgsqlTypes;
using OpenArchival.DataAccess;
using OpenArchival.DataAccess;
using System;
using System.Collections.Generic;
using System.Drawing.Printing;
using System.Linq;
using System.Linq;
using System.Linq.Expressions;
using System.Text;
using System.Threading.Tasks;

namespace OpenArchival.Blazor.ArchiveSearch;

public enum ArchiveSearchFilterType
{
    All,
    Tags,
    Defects,
    ListedNames,
    Title,
    Description,
    Filenames,
    ArtifactTranscriptions
}

public class ArtifactGroupingSearch
{

    public int TotalResults { get; set; }

    public int TotalPages { get; set; }

    public int PageSize { get; set; } = 20;

    public int CurrentPage { get; set; }

    public List<ArtifactGrouping> SearchResults { get; set; } = [];

    private string _searchTerms { get; set; }

    private IDbContextFactory<ApplicationDbContext> _contextFactory { get; set; }

    private Expression<Func<ArtifactGrouping, bool>> _currentFilterPredicate;

    private ArchiveSearchFilterType _selectedFilter = ArchiveSearchFilterType.All;

    public ArtifactGroupingSearch(IDbContextFactory<ApplicationDbContext> contextFactory)
    {
        _contextFactory = contextFactory;
    }

    public async Task Search(string Terms, ArchiveSearchFilterType filter, int page = 1)
    {
        _searchTerms = Terms;
        _selectedFilter = filter;

        // Determine which filter expression to use based on the radio button selection
        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;
        }
        
        // Get the total count using the chosen filter
        await using var context = await _contextFactory.CreateDbContextAsync();
        TotalResults = await context.ArtifactGroupings.Where(_currentFilterPredicate).CountAsync();
        TotalPages = (int)Math.Ceiling(TotalResults / (double)PageSize);

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

    public async Task LoadPageAsync(int page)
    {
        CurrentPage = page;

        if (_currentFilterPredicate == null) // Don't run if no search has been performed
        {
            return;
        }

        await using var context = await _contextFactory.CreateDbContextAsync();

        // The query uses the dynamically set filter predicate
        SearchResults = await context.ArtifactGroupings
                                     .Where(_currentFilterPredicate)
                                     .Include(x => x.ChildArtifactEntries)
                                     .ThenInclude(x => x.Files)
                                     .Include(x=>x.Category)
                                     .OrderBy(x => x.Id)
                                     .Skip((CurrentPage - 1) * PageSize)
                                     .Take(PageSize)
                                     .ToListAsync();

    }

    public void ClearResults()
    {
        SearchResults.Clear();
        TotalResults = 0;
        TotalPages = 0;
    }
}
