using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Models.Enums;
using MilitaryTrainingManagement.Services.Implementations;
using MilitaryTrainingManagement.Services.Interfaces;

namespace MilitaryTrainingManagement.Tests.Services;

/// <summary>
/// 审计服务单元测试
/// 测试审计日志记录和查询功能
/// _需求：1.4, 4.3, 5.4, 6.4_
/// </summary>
public class AuditServiceTests
{
    private static ApplicationDbContext CreateInMemoryContext(string dbName)
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: dbName)
            .Options;
        return new ApplicationDbContext(options);
    }

    private static AuditService CreateAuditService(ApplicationDbContext context)
    {
        var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole());
        var logger = loggerFactory.CreateLogger<AuditService>();
        return new AuditService(context, logger);
    }

    private static (OrganizationalUnit division, UserAccount user) CreateTestData(ApplicationDbContext context)
    {
        var division = new OrganizationalUnit
        {
            Name = "测试师团",
            Level = OrganizationalLevel.Division,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(division);
        context.SaveChanges();

        var user = new UserAccount
        {
            Username = "test_user",
            PasswordHash = "hash",
            DisplayName = "测试用户",
            OrganizationalUnitId = division.Id,
            IsActive = true,
            CreatedAt = DateTime.UtcNow
        };
        context.UserAccounts.Add(user);
        context.SaveChanges();

        return (division, user);
    }

    #region 审计日志记录测试 (需求1.4)

    /// <summary>
    /// 测试记录审计日志成功
    /// </summary>
    [Fact]
    public async Task LogAsync_WithValidEntry_CreatesAuditLog()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        var entry = new AuditLogEntry
        {
            EntityType = "TestEntity",
            EntityId = 1,
            Action = AuditActions.Create,
            Description = "创建测试记录",
            NewValues = "{\"name\": \"test\"}",
            UserId = user.Id,
            OrganizationalUnitId = division.Id,
            IpAddress = "127.0.0.1"
        };

        // Act
        await service.LogAsync(entry);

        // Assert
        var log = await context.AuditLogs.FirstOrDefaultAsync();
        Assert.NotNull(log);
        Assert.Equal("TestEntity", log.EntityType);
        Assert.Equal(1, log.EntityId);
        Assert.Equal(AuditActions.Create, log.Action);
        Assert.Equal(user.Id, log.UserId);
    }


    /// <summary>
    /// 测试使用简化方法记录审计日志
    /// </summary>
    [Fact]
    public async Task LogAsync_WithSimpleParameters_CreatesAuditLog()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // Act
        await service.LogAsync("TestEntity", 1, AuditActions.Update, 
            "{\"old\": \"value\"}", "{\"new\": \"value\"}", user.Id, "127.0.0.1");

        // Assert
        var log = await context.AuditLogs.FirstOrDefaultAsync();
        Assert.NotNull(log);
        Assert.Equal("TestEntity", log.EntityType);
        Assert.Equal(AuditActions.Update, log.Action);
        Assert.Equal("{\"old\": \"value\"}", log.OldValues);
        Assert.Equal("{\"new\": \"value\"}", log.NewValues);
    }

    /// <summary>
    /// 测试记录创建操作
    /// </summary>
    [Fact]
    public async Task LogCreateAsync_WithEntity_CreatesAuditLogWithNewValues()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        var personnel = new Personnel
        {
            Id = 1,
            Name = "测试人员",
            Position = "测试职位",
            Rank = "测试军衔",
            Gender = "男",
            IdNumber = "123456789012345678",
            Age = 25,
            Height = 175,
            SubmittedByUnitId = division.Id,
            Status = PersonnelStatus.Pending,
            SubmittedAt = DateTime.UtcNow
        };

        // Act
        await service.LogCreateAsync(personnel, user.Id, division.Id, "127.0.0.1");

        // Assert
        var log = await context.AuditLogs.FirstOrDefaultAsync();
        Assert.NotNull(log);
        Assert.Equal("Personnel", log.EntityType);
        Assert.Equal(1, log.EntityId);
        Assert.Equal(AuditActions.Create, log.Action);
        Assert.NotNull(log.NewValues);
        Assert.Null(log.OldValues);
    }

    /// <summary>
    /// 测试记录删除操作
    /// </summary>
    [Fact]
    public async Task LogDeleteAsync_WithEntity_CreatesAuditLogWithOldValues()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        var personnel = new Personnel
        {
            Id = 1,
            Name = "测试人员",
            Position = "测试职位",
            Rank = "测试军衔",
            Gender = "男",
            IdNumber = "123456789012345678",
            Age = 25,
            Height = 175,
            SubmittedByUnitId = division.Id,
            Status = PersonnelStatus.Pending,
            SubmittedAt = DateTime.UtcNow
        };

        // Act
        await service.LogDeleteAsync(personnel, user.Id, division.Id, "127.0.0.1");

        // Assert
        var log = await context.AuditLogs.FirstOrDefaultAsync();
        Assert.NotNull(log);
        Assert.Equal("Personnel", log.EntityType);
        Assert.Equal(AuditActions.Delete, log.Action);
        Assert.NotNull(log.OldValues);
        Assert.Null(log.NewValues);
    }

    /// <summary>
    /// 测试记录审批操作
    /// </summary>
    [Fact]
    public async Task LogApprovalAsync_CreatesAuditLogWithApprovalAction()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // Act
        await service.LogApprovalAsync("Personnel", 1, AuditActions.Approve, 
            "审批人员记录", user.Id, division.Id, "127.0.0.1");

        // Assert
        var log = await context.AuditLogs.FirstOrDefaultAsync();
        Assert.NotNull(log);
        Assert.Equal("Personnel", log.EntityType);
        Assert.Equal(AuditActions.Approve, log.Action);
        Assert.Equal("审批人员记录", log.Description);
    }

    /// <summary>
    /// 测试记录失败操作
    /// </summary>
    [Fact]
    public async Task LogFailureAsync_CreatesAuditLogWithErrorMessage()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // Act
        await service.LogFailureAsync("Personnel", 1, AuditActions.Update, 
            "数据验证失败", user.Id, division.Id, "127.0.0.1");

        // Assert
        var log = await context.AuditLogs.FirstOrDefaultAsync();
        Assert.NotNull(log);
        Assert.False(log.IsSuccess);
        Assert.Equal("数据验证失败", log.ErrorMessage);
    }

    #endregion


    #region 审计日志查询测试 (需求4.3)

    /// <summary>
    /// 测试获取实体历史
    /// </summary>
    [Fact]
    public async Task GetEntityHistoryAsync_ReturnsEntityLogs()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // 创建多个日志
        await service.LogAsync("Personnel", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 1, AuditActions.Update, "{}", "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 2, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");

        // Act
        var logs = await service.GetEntityHistoryAsync("Personnel", 1);

        // Assert
        Assert.Equal(2, logs.Count());
        Assert.All(logs, l => Assert.Equal(1, l.EntityId));
    }

    /// <summary>
    /// 测试获取用户活动历史
    /// </summary>
    [Fact]
    public async Task GetUserActivityAsync_ReturnsUserLogs()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // 创建另一个用户
        var user2 = new UserAccount
        {
            Username = "test_user2",
            PasswordHash = "hash",
            DisplayName = "测试用户2",
            OrganizationalUnitId = division.Id,
            IsActive = true,
            CreatedAt = DateTime.UtcNow
        };
        context.UserAccounts.Add(user2);
        await context.SaveChangesAsync();

        // 创建日志
        await service.LogAsync("Personnel", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 2, AuditActions.Create, null, "{}", user2.Id, "127.0.0.1");

        // Act
        var logs = await service.GetUserActivityAsync(user.Id);

        // Assert
        Assert.Single(logs);
        Assert.All(logs, l => Assert.Equal(user.Id, l.UserId));
    }

    /// <summary>
    /// 测试获取组织单位活动历史
    /// </summary>
    [Fact]
    public async Task GetOrganizationalUnitActivityAsync_ReturnsUnitLogs()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // 创建另一个组织单位
        var regiment = new OrganizationalUnit
        {
            Name = "测试团",
            Level = OrganizationalLevel.Regiment,
            ParentId = division.Id,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(regiment);
        await context.SaveChangesAsync();

        // 创建日志
        await service.LogAsync(new AuditLogEntry
        {
            EntityType = "Personnel",
            EntityId = 1,
            Action = AuditActions.Create,
            UserId = user.Id,
            OrganizationalUnitId = division.Id
        });
        await service.LogAsync(new AuditLogEntry
        {
            EntityType = "Personnel",
            EntityId = 2,
            Action = AuditActions.Create,
            UserId = user.Id,
            OrganizationalUnitId = regiment.Id
        });

        // Act
        var logs = await service.GetOrganizationalUnitActivityAsync(division.Id);

        // Assert
        Assert.Single(logs);
        Assert.All(logs, l => Assert.Equal(division.Id, l.OrganizationalUnitId));
    }

    /// <summary>
    /// 测试按时间范围查询
    /// </summary>
    [Fact]
    public async Task GetLogsAsync_WithDateRange_ReturnsFilteredLogs()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        var now = DateTime.UtcNow;

        // 创建日志
        await service.LogAsync("Personnel", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");

        // Act
        var logsInRange = await service.GetLogsAsync(
            entityType: "Personnel",
            fromDate: now.AddMinutes(-1),
            toDate: now.AddMinutes(1));

        var logsOutOfRange = await service.GetLogsAsync(
            entityType: "Personnel",
            fromDate: now.AddDays(-2),
            toDate: now.AddDays(-1));

        // Assert
        Assert.Single(logsInRange);
        Assert.Empty(logsOutOfRange);
    }

    /// <summary>
    /// 测试使用查询参数获取日志
    /// </summary>
    [Fact]
    public async Task GetLogsAsync_WithQueryParameters_ReturnsFilteredLogs()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // 创建多个日志
        await service.LogAsync("Personnel", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 2, AuditActions.Update, "{}", "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Allocation", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");

        var parameters = new AuditLogQueryParameters
        {
            EntityType = "Personnel",
            Action = AuditActions.Create
        };

        // Act
        var logs = await service.GetLogsAsync(parameters);

        // Assert
        Assert.Single(logs);
        Assert.All(logs, l => 
        {
            Assert.Equal("Personnel", l.EntityType);
            Assert.Equal(AuditActions.Create, l.Action);
        });
    }

    #endregion


    #region 审计日志统计测试

    /// <summary>
    /// 测试获取审计日志统计
    /// </summary>
    [Fact]
    public async Task GetStatisticsAsync_ReturnsCorrectStatistics()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // 创建不同类型的日志
        await service.LogAsync("Personnel", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 2, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 1, AuditActions.Update, "{}", "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 3, AuditActions.Delete, "{}", null, user.Id, "127.0.0.1");
        await service.LogApprovalAsync("Personnel", 1, AuditActions.Approve, "审批", user.Id, division.Id, "127.0.0.1");
        await service.LogFailureAsync("Personnel", 4, AuditActions.Create, "错误", user.Id, division.Id, "127.0.0.1");

        // Act
        var stats = await service.GetStatisticsAsync();

        // Assert
        Assert.Equal(6, stats.TotalLogs);
        Assert.Equal(3, stats.CreateOperations); // 2 successful + 1 failed
        Assert.Equal(1, stats.UpdateOperations);
        Assert.Equal(1, stats.DeleteOperations);
        Assert.Equal(1, stats.ApprovalOperations);
        Assert.Equal(1, stats.FailedOperations);
    }

    /// <summary>
    /// 测试按时间范围获取统计
    /// </summary>
    [Fact]
    public async Task GetStatisticsAsync_WithDateRange_ReturnsFilteredStatistics()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        var now = DateTime.UtcNow;

        // 创建日志
        await service.LogAsync("Personnel", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 2, AuditActions.Update, "{}", "{}", user.Id, "127.0.0.1");

        // Act
        var statsInRange = await service.GetStatisticsAsync(now.AddMinutes(-1), now.AddMinutes(1));
        var statsOutOfRange = await service.GetStatisticsAsync(now.AddDays(-2), now.AddDays(-1));

        // Assert
        Assert.Equal(2, statsInRange.TotalLogs);
        Assert.Equal(0, statsOutOfRange.TotalLogs);
    }

    /// <summary>
    /// 测试统计按实体类型分组
    /// </summary>
    [Fact]
    public async Task GetStatisticsAsync_GroupsByEntityType()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // 创建不同实体类型的日志
        await service.LogAsync("Personnel", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 2, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Allocation", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");

        // Act
        var stats = await service.GetStatisticsAsync();

        // Assert
        Assert.Equal(2, stats.OperationsByEntityType["Personnel"]);
        Assert.Equal(1, stats.OperationsByEntityType["Allocation"]);
    }

    /// <summary>
    /// 测试统计按操作类型分组
    /// </summary>
    [Fact]
    public async Task GetStatisticsAsync_GroupsByAction()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // 创建不同操作类型的日志
        await service.LogAsync("Personnel", 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 1, AuditActions.Update, "{}", "{}", user.Id, "127.0.0.1");
        await service.LogAsync("Personnel", 1, AuditActions.Update, "{}", "{}", user.Id, "127.0.0.1");

        // Act
        var stats = await service.GetStatisticsAsync();

        // Assert
        Assert.Equal(1, stats.OperationsByAction[AuditActions.Create]);
        Assert.Equal(2, stats.OperationsByAction[AuditActions.Update]);
    }

    #endregion

    #region 分页测试

    /// <summary>
    /// 测试分页查询
    /// </summary>
    [Fact]
    public async Task GetLogsAsync_WithPagination_ReturnsPagedResults()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = CreateAuditService(context);
        var (division, user) = CreateTestData(context);

        // 创建多个日志
        for (int i = 0; i < 10; i++)
        {
            await service.LogAsync("Personnel", i + 1, AuditActions.Create, null, "{}", user.Id, "127.0.0.1");
        }

        var parameters = new AuditLogQueryParameters
        {
            PageNumber = 1,
            PageSize = 5
        };

        // Act
        var page1 = await service.GetLogsAsync(parameters);
        
        parameters.PageNumber = 2;
        var page2 = await service.GetLogsAsync(parameters);

        // Assert
        Assert.Equal(5, page1.Count());
        Assert.Equal(5, page2.Count());
    }

    #endregion
}
