using e_suite.API.Common;
using e_suite.API.Common.models;
using e_suite.API.Common.repository;
using e_suite.Utilities.Pagination;
using eSuite.Core.Clock;
using Newtonsoft.Json;
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;

namespace e_suite.Modules.ExceptionLogManager;

public class ExceptionLogger : IExceptionLogManager
{
    private readonly IClock _clock;
    private readonly IExceptionLogRepository _exceptionLogRepository;

    public ExceptionLogger(IClock clock, IExceptionLogRepository exceptionLogRepository)
    {
        _clock = clock;
        _exceptionLogRepository = exceptionLogRepository;
    }

    public async Task<IPaginatedData<ExceptionLog>> GetExceptionLogs(Paging paging, CancellationToken cancellationToken)
    {
        var exceptionLogs = _exceptionLogRepository.GetExceptionLogs();

        var paginatedData = await PaginatedData.Paginate<e_suite.Database.Core.Tables.Diagnostics.ExceptionLog, ExceptionLog>(exceptionLogs, paging,
            KeySelector, cancellationToken);

        return paginatedData;
    }

    private Expression<Func<Database.Core.Tables.Diagnostics.ExceptionLog, object>> KeySelector(string? sortKey)
    {
        return sortKey?.ToLowerInvariant() switch
        {
            "id" => x => x.Id,
            "application" => x => x.Application,
            "exceptionJson" => x => x.ExceptionJson,
            "message" => x => x.Message,
            "stackTrace" => x => x.StackTrace,
            "supportingData" => x => x.SupportingData,
            "occuredAt" => x => x.OccuredAt,
            _ => x => x.Id
        };
    }

    public async Task<long> LogException(Exception ex, string application, string supportingArgs, CancellationToken cancellationToken)
    {
        var exceptionLog = new Database.Core.Tables.Diagnostics.ExceptionLog
        {
            Application = application,
            Message = ex.Message,
            StackTrace = ex.StackTrace ?? string.Empty,
            ExceptionJson = JsonConvert.SerializeObject(ex),
            SupportingData = supportingArgs,
            OccuredAt = _clock.GetNow
        };

        var exceptionId = await _exceptionLogRepository.LogException(exceptionLog, cancellationToken);
        return exceptionId;
    }
}
