@page "/logging/general"
@using AliasVault.RazorComponents.Tables
@inherits MainBase

<LayoutPageTitle>System logs</LayoutPageTitle>

<PageHeader
    BreadcrumbItems="@BreadcrumbItems"
    Title="@(TotalRecords > 0 ? $"General logs ({TotalRecords:N0})" : "General logs")"
    Description="This page shows an overview of recent system logs.">
    <CustomActions>
        <DeleteButton OnClick="DeleteLogsWithConfirmation" ButtonText="Delete all logs" />
        <RefreshButton OnClick="RefreshData" ButtonText="Refresh" />
    </CustomActions>
</PageHeader>

@if (IsInitialized)
{
    <div class="px-4">
        <Paginator CurrentPage="CurrentPage" PageSize="PageSize" TotalRecords="TotalRecords" OnPageChanged="HandlePageChanged" />

       <div class="mb-3 flex space-x-4">
            <div class="flex w-full">
                <div class="w-2/3 pr-2">
                    <div class="relative">
                        <SearchIcon />
                        <input type="text" @bind-value="SearchTerm" @bind-value:event="oninput" id="search" placeholder="Search logs..." class="w-full px-4 ps-10 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white">
                    </div>
                </div>
                <div class="w-1/3 pl-2">
                    <select @bind="SelectedServiceName" class="w-full px-4 py-2 border rounded text-sm text-gray-700 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white">
                        <option value="">All Services</option>
                        @foreach (var service in ServiceNames)
                        {
                            <option value="@service">@service</option>
                        }
                    </select>
                </div>
            </div>
        </div>
    </div>
}

@if (IsLoading)
{
    <LoadingIndicator />
}
else
{
    <div class="px-4">
        <SortableTable Columns="@_tableColumns" SortColumn="@SortColumn" SortDirection="@SortDirection" OnSortChanged="HandleSortChanged">
            @foreach (var log in LogList)
            {
                <SortableTableRow>
                    <SortableTableColumn IsPrimary="true">@log.Id</SortableTableColumn>
                    <SortableTableColumn>@log.TimeStamp.ToString("yyyy-MM-dd HH:mm")</SortableTableColumn>
                    <SortableTableColumn>@log.Application</SortableTableColumn>
                    <SortableTableColumn>
                        @{
                            string bgColor = log.Level switch
                            {
                                "Information" => "bg-blue-500",
                                "Error" => "bg-red-500",
                                "Warning" => "bg-yellow-500",
                                "Debug" => "bg-green-500",
                                _ => "bg-gray-500"
                            };
                        }
                        <span class="px-2 py-1 rounded-full text-white @bgColor">
                            @log.Level
                        </span>
                    </SortableTableColumn>
                    <SortableTableColumn Title="@log.Exception">
                        @if (log.SourceContext.Length > 0)
                        {
                            <span>@log.SourceContext: </span>
                        }
                        @log.Message
                    </SortableTableColumn>
                </SortableTableRow>
            }
        </SortableTable>
    </div>
}

@code {
    private readonly List<TableColumn> _tableColumns = [
        new TableColumn { Title = "ID", PropertyName = "Id" },
        new TableColumn { Title = "Time", PropertyName = "Timestamp" },
        new TableColumn { Title = "Application", PropertyName = "Application" },
        new TableColumn { Title = "Level", PropertyName = "Level" },
        new TableColumn { Title = "Message", PropertyName = "Message" },
    ];

    private List<Log> LogList { get; set; } = [];
    private bool IsInitialized { get; set; } = false;

    private bool IsLoading { get; set; } = true;
    private int CurrentPage { get; set; } = 1;
    private int PageSize { get; set; } = 50;
    private int TotalRecords { get; set; }

    private string _searchTerm = string.Empty;
    private string _lastSearchTerm = string.Empty;
    private string SearchTerm
    {
        get => _searchTerm;
        set
        {
            if (_searchTerm != value)
            {
                _searchTerm = value;
                _ = RefreshData();
            }
        }
    }

    private string _selectedServiceName = string.Empty;
    private string SelectedServiceName
    {
        get => _selectedServiceName;
        set
        {
            if (_selectedServiceName != value)
            {
                _selectedServiceName = value;
                _ = RefreshData();
            }
        }
    }

    private List<string> ServiceNames { get; set; } = [];

    private string SortColumn { get; set; } = "Id";
    private SortDirection SortDirection { get; set; } = SortDirection.Descending;

    private async Task HandleSortChanged((string column, SortDirection direction) sort)
    {
        SortColumn = sort.column;
        SortDirection = sort.direction;
        await RefreshData();
    }

    /// <inheritdoc />
    protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        BreadcrumbItems.Add(new BreadcrumbItem { DisplayName = "General logs" });
    }

    /// <inheritdoc />
    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await using var dbContext = await DbContextFactory.CreateDbContextAsync();
            ServiceNames = await dbContext.Logs.Select(l => l.Application).Distinct().ToListAsync();
            await RefreshData();
        }
    }

    private void HandlePageChanged(int newPage)
    {
        CurrentPage = newPage;
        _ = RefreshData();
    }

    private async Task RefreshData()
    {
        IsLoading = true;
        StateHasChanged();

        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        var query = dbContext.Logs.AsQueryable();

        query = ApplySearchTermFilter(query);
        query = ApplyServiceNameFilter(query);

        // Apply sort.
        switch (SortColumn)
        {
            case "Application":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Application)
                    : query.OrderByDescending(x => x.Application);
                break;
            case "Message":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Message)
                    : query.OrderByDescending(x => x.Message);
                break;
            case "Level":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Level)
                    : query.OrderByDescending(x => x.Level);
                break;
            case "Timestamp":
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.TimeStamp)
                    : query.OrderByDescending(x => x.TimeStamp);
                break;
            default:
                query = SortDirection == SortDirection.Ascending
                    ? query.OrderBy(x => x.Id)
                    : query.OrderByDescending(x => x.Id);
                break;
        }

        TotalRecords = await query.CountAsync();
        LogList = await query
            .Skip((CurrentPage - 1) * PageSize)
            .Take(PageSize)
            .ToListAsync();

        IsLoading = false;
        IsInitialized = true;
        StateHasChanged();
    }

    /// <summary>
    /// Applies a search term filter to the query.
    /// </summary>
    /// <param name="query">The query to apply the filter to.</param>
    private IQueryable<Log> ApplySearchTermFilter(IQueryable<Log> query)
    {
        if (!string.IsNullOrEmpty(SearchTerm))
        {
            // Reset page number back to 1 if the search term has changed.
            if (SearchTerm != _lastSearchTerm)
            {
                CurrentPage = 1;
            }
            _lastSearchTerm = SearchTerm;

            var searchTerm = SearchTerm.Trim().ToLower();
            query = query.Where(x => EF.Functions.Like(x.Application.ToLower(), "%" + searchTerm + "%") ||
                                     EF.Functions.Like(x.Message.ToLower(), "%" + searchTerm + "%") ||
                                     EF.Functions.Like(x.Level.ToLower(), "%" + searchTerm + "%") ||
                                     EF.Functions.Like(x.SourceContext.ToLower(), "%" + searchTerm + "%"));
        }

        return query;
    }

    /// <summary>
    /// Applies a service name filter to the query.
    /// </summary>
    /// <param name="query">The query to apply the filter to.</param>
    private IQueryable<Log> ApplyServiceNameFilter(IQueryable<Log> query)
    {
        if (!string.IsNullOrEmpty(SelectedServiceName))
        {
            query = query.Where(x => x.Application == SelectedServiceName);
        }

        return query;
    }

    private async Task DeleteLogsWithConfirmation()
    {
        if (await ConfirmModalService.ShowConfirmation("Confirm Delete", "Are you sure you want to delete all logs? This action cannot be undone."))
        {
            await DeleteLogs();
        }
    }

    private async Task DeleteLogs()
    {
        IsLoading = true;
        StateHasChanged();

        await using var dbContext = await DbContextFactory.CreateDbContextAsync();
        dbContext.Logs.RemoveRange(dbContext.Logs);
        await dbContext.SaveChangesAsync();
        await RefreshData();

        IsLoading = false;
        StateHasChanged();
    }
}
