using System.Reflection;
using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Services.Interfaces;

namespace MilitaryTrainingManagement.Services.Implementations;

/// <summary>
/// 审计服务实现
/// </summary>
public class AuditService : IAuditService
{
    private readonly ApplicationDbContext _context;
    private readonly ILogger<AuditService> _logger;
    private static readonly JsonSerializerOptions JsonOptions = new()
    {
        WriteIndented = false,
        PropertyNamingPolicy = JsonNamingPolicy.CamelCase
    };

    public AuditService(ApplicationDbContext context, ILogger<AuditService> logger)
    {
        _context = context;
        _logger = logger;
    }

    public async Task LogAsync(AuditLogEntry entry)
    {
        var log = new AuditLog
        {
            EntityType = entry.EntityType,
            EntityId = entry.EntityId,
            Action = entry.Action,
            Description = entry.Description,
            OldValues = entry.OldValues,
            NewValues = entry.NewValues,
            ChangedFields = entry.ChangedFields,
            UserId = entry.UserId,
            OrganizationalUnitId = entry.OrganizationalUnitId,
            IpAddress = entry.IpAddress,
            UserAgent = entry.UserAgent,
            RequestPath = entry.RequestPath,
            IsSuccess = entry.IsSuccess,
            ErrorMessage = entry.ErrorMessage,
            Timestamp = DateTime.UtcNow
        };

        _context.AuditLogs.Add(log);
        await _context.SaveChangesAsync();
        
        _logger.LogInformation("审计日志已记录: {EntityType} {EntityId} {Action}", 
            entry.EntityType, entry.EntityId, entry.Action);
    }

    public async Task LogAsync(string entityType, int entityId, string action, string? oldValues, string? newValues, int? userId, string? ipAddress)
    {
        await LogAsync(new AuditLogEntry
        {
            EntityType = entityType,
            EntityId = entityId,
            Action = action,
            OldValues = oldValues,
            NewValues = newValues,
            UserId = userId,
            IpAddress = ipAddress
        });
    }


    public async Task LogCreateAsync<T>(T entity, int? userId, int? organizationalUnitId, string? ipAddress = null, string? userAgent = null, string? requestPath = null) where T : class
    {
        var entityType = typeof(T).Name;
        var entityId = GetEntityId(entity);
        var newValues = SerializeEntity(entity);

        await LogAsync(new AuditLogEntry
        {
            EntityType = entityType,
            EntityId = entityId,
            Action = AuditActions.Create,
            Description = $"创建{GetEntityDisplayName(entityType)}记录",
            NewValues = newValues,
            UserId = userId,
            OrganizationalUnitId = organizationalUnitId,
            IpAddress = ipAddress,
            UserAgent = userAgent,
            RequestPath = requestPath
        });
    }

    public async Task LogUpdateAsync<T>(T originalEntity, T updatedEntity, int? userId, int? organizationalUnitId, string? ipAddress = null, string? userAgent = null, string? requestPath = null) where T : class
    {
        var entityType = typeof(T).Name;
        var entityId = GetEntityId(updatedEntity);
        var oldValues = SerializeEntity(originalEntity);
        var newValues = SerializeEntity(updatedEntity);
        var changedFields = GetChangedFields(originalEntity, updatedEntity);

        await LogAsync(new AuditLogEntry
        {
            EntityType = entityType,
            EntityId = entityId,
            Action = AuditActions.Update,
            Description = $"更新{GetEntityDisplayName(entityType)}记录",
            OldValues = oldValues,
            NewValues = newValues,
            ChangedFields = JsonSerializer.Serialize(changedFields, JsonOptions),
            UserId = userId,
            OrganizationalUnitId = organizationalUnitId,
            IpAddress = ipAddress,
            UserAgent = userAgent,
            RequestPath = requestPath
        });
    }

    public async Task LogDeleteAsync<T>(T entity, int? userId, int? organizationalUnitId, string? ipAddress = null, string? userAgent = null, string? requestPath = null) where T : class
    {
        var entityType = typeof(T).Name;
        var entityId = GetEntityId(entity);
        var oldValues = SerializeEntity(entity);

        await LogAsync(new AuditLogEntry
        {
            EntityType = entityType,
            EntityId = entityId,
            Action = AuditActions.Delete,
            Description = $"删除{GetEntityDisplayName(entityType)}记录",
            OldValues = oldValues,
            UserId = userId,
            OrganizationalUnitId = organizationalUnitId,
            IpAddress = ipAddress,
            UserAgent = userAgent,
            RequestPath = requestPath
        });
    }

    public async Task LogApprovalAsync(string entityType, int entityId, string action, string? description, int? userId, int? organizationalUnitId, string? ipAddress = null)
    {
        await LogAsync(new AuditLogEntry
        {
            EntityType = entityType,
            EntityId = entityId,
            Action = action,
            Description = description ?? $"{action}{GetEntityDisplayName(entityType)}记录",
            UserId = userId,
            OrganizationalUnitId = organizationalUnitId,
            IpAddress = ipAddress
        });
    }

    public async Task LogFailureAsync(string entityType, int entityId, string action, string errorMessage, int? userId, int? organizationalUnitId, string? ipAddress = null)
    {
        await LogAsync(new AuditLogEntry
        {
            EntityType = entityType,
            EntityId = entityId,
            Action = action,
            Description = $"{action}操作失败",
            UserId = userId,
            OrganizationalUnitId = organizationalUnitId,
            IpAddress = ipAddress,
            IsSuccess = false,
            ErrorMessage = errorMessage
        });
    }


    public async Task<IEnumerable<AuditLog>> GetLogsAsync(AuditLogQueryParameters parameters)
    {
        var query = _context.AuditLogs
            .Include(l => l.User)
            .Include(l => l.OrganizationalUnit)
            .AsQueryable();

        if (!string.IsNullOrEmpty(parameters.EntityType))
            query = query.Where(l => l.EntityType == parameters.EntityType);

        if (parameters.EntityId.HasValue)
            query = query.Where(l => l.EntityId == parameters.EntityId.Value);

        if (!string.IsNullOrEmpty(parameters.Action))
            query = query.Where(l => l.Action == parameters.Action);

        if (parameters.UserId.HasValue)
            query = query.Where(l => l.UserId == parameters.UserId.Value);

        if (parameters.OrganizationalUnitId.HasValue)
            query = query.Where(l => l.OrganizationalUnitId == parameters.OrganizationalUnitId.Value);

        if (parameters.FromDate.HasValue)
            query = query.Where(l => l.Timestamp >= parameters.FromDate.Value);

        if (parameters.ToDate.HasValue)
            query = query.Where(l => l.Timestamp <= parameters.ToDate.Value);

        if (parameters.IsSuccess.HasValue)
            query = query.Where(l => l.IsSuccess == parameters.IsSuccess.Value);

        return await query
            .OrderByDescending(l => l.Timestamp)
            .Skip((parameters.PageNumber - 1) * parameters.PageSize)
            .Take(parameters.PageSize)
            .ToListAsync();
    }

    public async Task<int> GetLogCountAsync(AuditLogQueryParameters parameters)
    {
        var query = _context.AuditLogs.AsQueryable();

        if (!string.IsNullOrEmpty(parameters.EntityType))
            query = query.Where(l => l.EntityType == parameters.EntityType);

        if (parameters.EntityId.HasValue)
            query = query.Where(l => l.EntityId == parameters.EntityId.Value);

        if (!string.IsNullOrEmpty(parameters.Action))
            query = query.Where(l => l.Action == parameters.Action);

        if (parameters.UserId.HasValue)
            query = query.Where(l => l.UserId == parameters.UserId.Value);

        if (parameters.OrganizationalUnitId.HasValue)
            query = query.Where(l => l.OrganizationalUnitId == parameters.OrganizationalUnitId.Value);

        if (parameters.FromDate.HasValue)
            query = query.Where(l => l.Timestamp >= parameters.FromDate.Value);

        if (parameters.ToDate.HasValue)
            query = query.Where(l => l.Timestamp <= parameters.ToDate.Value);

        if (parameters.IsSuccess.HasValue)
            query = query.Where(l => l.IsSuccess == parameters.IsSuccess.Value);

        return await query.CountAsync();
    }

    public async Task<AuditLog?> GetLogByIdAsync(int id)
    {
        return await _context.AuditLogs
            .Include(l => l.User)
            .Include(l => l.OrganizationalUnit)
            .FirstOrDefaultAsync(l => l.Id == id);
    }

    public async Task<IEnumerable<AuditLog>> GetLogsAsync(string? entityType = null, int? entityId = null, DateTime? fromDate = null, DateTime? toDate = null)
    {
        return await GetLogsAsync(new AuditLogQueryParameters
        {
            EntityType = entityType,
            EntityId = entityId,
            FromDate = fromDate,
            ToDate = toDate
        });
    }

    public async Task<IEnumerable<AuditLog>> GetEntityHistoryAsync(string entityType, int entityId)
    {
        return await _context.AuditLogs
            .Include(l => l.User)
            .Include(l => l.OrganizationalUnit)
            .Where(l => l.EntityType == entityType && l.EntityId == entityId)
            .OrderByDescending(l => l.Timestamp)
            .ToListAsync();
    }

    public async Task<IEnumerable<AuditLog>> GetUserActivityAsync(int userId, DateTime? fromDate = null, DateTime? toDate = null)
    {
        var query = _context.AuditLogs
            .Include(l => l.OrganizationalUnit)
            .Where(l => l.UserId == userId);

        if (fromDate.HasValue)
            query = query.Where(l => l.Timestamp >= fromDate.Value);

        if (toDate.HasValue)
            query = query.Where(l => l.Timestamp <= toDate.Value);

        return await query.OrderByDescending(l => l.Timestamp).ToListAsync();
    }

    public async Task<IEnumerable<AuditLog>> GetOrganizationalUnitActivityAsync(int organizationalUnitId, DateTime? fromDate = null, DateTime? toDate = null)
    {
        var query = _context.AuditLogs
            .Include(l => l.User)
            .Where(l => l.OrganizationalUnitId == organizationalUnitId);

        if (fromDate.HasValue)
            query = query.Where(l => l.Timestamp >= fromDate.Value);

        if (toDate.HasValue)
            query = query.Where(l => l.Timestamp <= toDate.Value);

        return await query.OrderByDescending(l => l.Timestamp).ToListAsync();
    }


    public async Task<AuditLogStatistics> GetStatisticsAsync(DateTime? fromDate = null, DateTime? toDate = null)
    {
        var query = _context.AuditLogs.AsQueryable();

        if (fromDate.HasValue)
            query = query.Where(l => l.Timestamp >= fromDate.Value);

        if (toDate.HasValue)
            query = query.Where(l => l.Timestamp <= toDate.Value);

        var logs = await query.ToListAsync();

        return new AuditLogStatistics
        {
            TotalLogs = logs.Count,
            CreateOperations = logs.Count(l => l.Action == AuditActions.Create),
            UpdateOperations = logs.Count(l => l.Action == AuditActions.Update),
            DeleteOperations = logs.Count(l => l.Action == AuditActions.Delete),
            ApprovalOperations = logs.Count(l => l.Action == AuditActions.Approve || l.Action == AuditActions.Reject),
            FailedOperations = logs.Count(l => !l.IsSuccess),
            OperationsByEntityType = logs.GroupBy(l => l.EntityType).ToDictionary(g => g.Key, g => g.Count()),
            OperationsByAction = logs.GroupBy(l => l.Action).ToDictionary(g => g.Key, g => g.Count())
        };
    }

    #region Helper Methods

    private static int GetEntityId<T>(T entity) where T : class
    {
        var idProperty = typeof(T).GetProperty("Id");
        if (idProperty != null)
        {
            var value = idProperty.GetValue(entity);
            if (value is int intValue)
                return intValue;
        }
        return 0;
    }

    private static string SerializeEntity<T>(T entity) where T : class
    {
        try
        {
            // 创建一个只包含简单属性的字典，避免循环引用
            var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
                .Where(p => IsSimpleType(p.PropertyType))
                .ToDictionary(p => p.Name, p => p.GetValue(entity));

            return JsonSerializer.Serialize(properties, JsonOptions);
        }
        catch (Exception)
        {
            return "{}";
        }
    }

    private static bool IsSimpleType(Type type)
    {
        var underlyingType = Nullable.GetUnderlyingType(type) ?? type;
        return underlyingType.IsPrimitive
            || underlyingType == typeof(string)
            || underlyingType == typeof(decimal)
            || underlyingType == typeof(DateTime)
            || underlyingType == typeof(DateTimeOffset)
            || underlyingType == typeof(Guid)
            || underlyingType.IsEnum;
    }

    private static List<string> GetChangedFields<T>(T original, T updated) where T : class
    {
        var changedFields = new List<string>();
        var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance)
            .Where(p => IsSimpleType(p.PropertyType));

        foreach (var property in properties)
        {
            var originalValue = property.GetValue(original);
            var updatedValue = property.GetValue(updated);

            if (!Equals(originalValue, updatedValue))
            {
                changedFields.Add(property.Name);
            }
        }

        return changedFields;
    }

    private static string GetEntityDisplayName(string entityType)
    {
        return entityType switch
        {
            "MaterialAllocation" => "物资配额",
            "AllocationDistribution" => "配额分配",
            "Personnel" => "人员",
            "OrganizationalUnit" => "组织单位",
            "UserAccount" => "用户账户",
            "ApprovalRequest" => "审批请求",
            _ => entityType
        };
    }

    #endregion
}

/// <summary>
/// 审计操作类型常量
/// </summary>
public static class AuditActions
{
    public const string Create = "Create";
    public const string Update = "Update";
    public const string Delete = "Delete";
    public const string Approve = "Approve";
    public const string Reject = "Reject";
    public const string Submit = "Submit";
    public const string Login = "Login";
    public const string Logout = "Logout";
    public const string Transfer = "Transfer";
    public const string Distribute = "Distribute";
    public const string Report = "Report";
}
