using NPin.Framework.AuditLogging.Domain.Entities;
using Volo.Abp.AspNetCore.ExceptionHandling;
using Volo.Abp.Auditing;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Guids;
using Volo.Abp.Json;

namespace NPin.Framework.AuditLogging.Domain;

public class AuditLogInfoToAuditLogConverter : IAuditLogInfoToAuditLogConverter, ITransientDependency
{
    protected IGuidGenerator GuidGenerator { get; }
    protected IExceptionToErrorInfoConverter ExceptionToErrorInfoConverter { get; }
    protected IJsonSerializer JsonSerializer { get; }
    protected AbpExceptionHandlingOptions ExceptionHandlingOptions { get; }

    public AuditLogInfoToAuditLogConverter(IGuidGenerator guidGenerator,
        IExceptionToErrorInfoConverter exceptionToErrorInfoConverter, IJsonSerializer jsonSerializer,
        AbpExceptionHandlingOptions exceptionHandlingOptions)
    {
        GuidGenerator = guidGenerator;
        ExceptionToErrorInfoConverter = exceptionToErrorInfoConverter;
        JsonSerializer = jsonSerializer;
        ExceptionHandlingOptions = exceptionHandlingOptions;
    }

    public virtual Task<AuditLogAggregateRoot> ConvertAsync(AuditLogInfo auditLogInfo)
    {
        var auditLogId = GuidGenerator.Create();

        var extraProperties = new ExtraPropertyDictionary();
        foreach (var pair in auditLogInfo.ExtraProperties)
        {
            extraProperties.Add(pair.Key, pair.Value);
        }

        var entityChanges = auditLogInfo.EntityChanges?
            .Select(info => new EntityChangeEntity(GuidGenerator, auditLogId, info, auditLogInfo.TenantId))
            .ToList() ?? [];
        var actions = auditLogInfo.Actions?
            .Select(info => new AuditLogActionEntity(GuidGenerator.Create(), auditLogId, info, auditLogInfo.TenantId))
            .ToList() ?? [];
        var remoteServiceErrorInfos = auditLogInfo.Exceptions?
            .Select(ex => ExceptionToErrorInfoConverter.Convert(ex, options =>
            {
                options.SendExceptionsDetailsToClients = ExceptionHandlingOptions.SendExceptionsDetailsToClients;
                options.SendStackTraceToClients = ExceptionHandlingOptions.SendStackTraceToClients;
            })) ?? [];

        var exceptions = remoteServiceErrorInfos.Any()
            ? JsonSerializer.Serialize(remoteServiceErrorInfos, indented: true)
            : null;

        var comments = auditLogInfo.Comments?
            .JoinAsString(Environment.NewLine);

        var auditLog = new AuditLogAggregateRoot(
            auditLogId,
            auditLogInfo.ApplicationName,
            auditLogInfo.UserId,
            auditLogInfo.UserName,
            auditLogInfo.TenantName,
            auditLogInfo.ImpersonatorUserId,
            auditLogInfo.ImpersonatorUserName,
            auditLogInfo.ImpersonatorTenantId,
            auditLogInfo.ImpersonatorTenantName,
            auditLogInfo.ExecutionTime,
            auditLogInfo.ExecutionDuration,
            auditLogInfo.ClientIpAddress,
            auditLogInfo.ClientName,
            auditLogInfo.ClientId,
            auditLogInfo.CorrelationId,
            auditLogInfo.BrowserInfo,
            auditLogInfo.HttpMethod,
            auditLogInfo.Url,
            exceptions,
            comments,
            auditLogInfo.HttpStatusCode,
            auditLogInfo.TenantId,
            entityChanges,
            actions,
            extraProperties
        );

        return Task.FromResult(auditLog);
    }
}