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

namespace MilitaryTrainingManagement.Tests.Services;

/// <summary>
/// 审批工作流服务单元测试
/// 测试工作流状态转换、审批权限验证和修改限制逻辑
/// _需求：5.1, 5.2, 5.3, 5.4, 5.5_
/// </summary>
public class WorkflowServiceTests
{
    private static ApplicationDbContext CreateInMemoryContext(string dbName)
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: dbName)
            .Options;
        return new ApplicationDbContext(options);
    }

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

        var regiment = new OrganizationalUnit
        {
            Name = "团",
            Level = OrganizationalLevel.Regiment,
            ParentId = division.Id,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(regiment);
        context.SaveChanges();

        var divisionUser = new UserAccount
        {
            Username = "division_admin",
            PasswordHash = "hash",
            DisplayName = "师团管理员",
            OrganizationalUnitId = division.Id,
            IsActive = true,
            CreatedAt = DateTime.UtcNow
        };
        context.UserAccounts.Add(divisionUser);

        var regimentUser = new UserAccount
        {
            Username = "regiment_user",
            PasswordHash = "hash",
            DisplayName = "团用户",
            OrganizationalUnitId = regiment.Id,
            IsActive = true,
            CreatedAt = DateTime.UtcNow
        };
        context.UserAccounts.Add(regimentUser);
        context.SaveChanges();

        return (division, regiment, divisionUser, regimentUser);
    }

    private static MaterialAllocation CreateAllocation(ApplicationDbContext context, int unitId)
    {
        var allocation = new MaterialAllocation
        {
            Category = "弹药",
            MaterialName = "测试物资",
            Unit = "件",
            TotalQuota = 100m,
            CreatedByUnitId = unitId,
            CreatedAt = DateTime.UtcNow
        };
        context.MaterialAllocations.Add(allocation);
        context.SaveChanges();
        return allocation;
    }

    private static AllocationDistribution CreateDistribution(
        ApplicationDbContext context, int allocationId, int targetUnitId, bool isApproved = false)
    {
        var distribution = new AllocationDistribution
        {
            AllocationId = allocationId,
            TargetUnitId = targetUnitId,
            UnitQuota = 50m,
            ActualCompletion = 30m,
            IsApproved = isApproved,
            ReportedAt = DateTime.UtcNow
        };
        context.AllocationDistributions.Add(distribution);
        context.SaveChanges();
        return distribution;
    }


    #region 工作流状态转换测试 (需求5.1, 5.2)

    /// <summary>
    /// 测试创建修改请求成功
    /// </summary>
    [Fact]
    public async Task CreateModificationRequestAsync_WithValidData_CreatesRequest()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        // Act
        var request = await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "数据录入错误",
            "{\"ActualCompletion\": 30}",
            "{\"ActualCompletion\": 35}");

        // Assert
        Assert.NotNull(request);
        Assert.Equal(ApprovalRequestType.ReportModification, request.Type);
        Assert.Equal(distribution.Id, request.TargetEntityId);
        Assert.Equal(regimentUser.Id, request.RequestedByUserId);
        Assert.Equal(regiment.Id, request.RequestedByUnitId);
        Assert.Equal("数据录入错误", request.Reason);
        Assert.Equal(ApprovalStatus.Pending, request.Status);
    }

    /// <summary>
    /// 测试创建修改请求时目标实体不存在
    /// </summary>
    [Fact]
    public async Task CreateModificationRequestAsync_WithNonExistingEntity_ThrowsArgumentException()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var service = new WorkflowService(context);

        // Act & Assert
        var exception = await Assert.ThrowsAsync<ArgumentException>(() =>
            service.CreateModificationRequestAsync(
                ApprovalRequestType.ReportModification,
                999,
                regimentUser.Id,
                regiment.Id,
                "测试原因",
                "{}",
                "{}"));

        Assert.Equal("目标实体不存在", exception.Message);
    }

    /// <summary>
    /// 测试创建修改请求时已有待处理请求
    /// </summary>
    [Fact]
    public async Task CreateModificationRequestAsync_WithPendingRequest_ThrowsInvalidOperationException()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        // 创建第一个请求
        await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "第一次请求",
            "{}",
            "{}");

        // Act & Assert
        var exception = await Assert.ThrowsAsync<InvalidOperationException>(() =>
            service.CreateModificationRequestAsync(
                ApprovalRequestType.ReportModification,
                distribution.Id,
                regimentUser.Id,
                regiment.Id,
                "第二次请求",
                "{}",
                "{}"));

        Assert.Equal("该记录已有待处理的修改请求", exception.Message);
    }

    #endregion


    #region 审批权限验证测试 (需求5.3)

    /// <summary>
    /// 测试师团级别用户可以审批
    /// </summary>
    [Fact]
    public async Task CanUserApproveAsync_WithDivisionUser_ReturnsTrue()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var service = new WorkflowService(context);

        // Act
        var canApprove = await service.CanUserApproveAsync(divisionUser.Id);

        // Assert
        Assert.True(canApprove);
    }

    /// <summary>
    /// 测试非师团级别用户不能审批
    /// </summary>
    [Fact]
    public async Task CanUserApproveAsync_WithNonDivisionUser_ReturnsFalse()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var service = new WorkflowService(context);

        // Act
        var canApprove = await service.CanUserApproveAsync(regimentUser.Id);

        // Assert
        Assert.False(canApprove);
    }

    /// <summary>
    /// 测试审批通过请求成功
    /// </summary>
    [Fact]
    public async Task ApproveAsync_WithDivisionUser_ApprovesRequest()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        var request = await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{\"ActualCompletion\": 30}",
            "{\"ActualCompletion\": 35}");

        // Act
        var approved = await service.ApproveAsync(request.Id, divisionUser.Id, "同意修改");

        // Assert
        Assert.Equal(ApprovalStatus.Approved, approved.Status);
        Assert.Equal(divisionUser.Id, approved.ReviewedByUserId);
        Assert.Equal("同意修改", approved.ReviewComments);
        Assert.NotNull(approved.ReviewedAt);
    }

    /// <summary>
    /// 测试非师团级别用户审批失败
    /// </summary>
    [Fact]
    public async Task ApproveAsync_WithNonDivisionUser_ThrowsUnauthorizedAccessException()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        var request = await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{}",
            "{}");

        // Act & Assert
        await Assert.ThrowsAsync<UnauthorizedAccessException>(() =>
            service.ApproveAsync(request.Id, regimentUser.Id, "尝试审批"));
    }

    /// <summary>
    /// 测试拒绝请求成功
    /// </summary>
    [Fact]
    public async Task RejectAsync_WithDivisionUser_RejectsRequest()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        var request = await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{}",
            "{}");

        // Act
        var rejected = await service.RejectAsync(request.Id, divisionUser.Id, "不符合要求");

        // Assert
        Assert.Equal(ApprovalStatus.Rejected, rejected.Status);
        Assert.Equal(divisionUser.Id, rejected.ReviewedByUserId);
        Assert.Equal("不符合要求", rejected.ReviewComments);
        Assert.NotNull(rejected.ReviewedAt);
    }

    #endregion


    #region 修改限制逻辑测试 (需求5.5)

    /// <summary>
    /// 测试已审批的上报记录不能直接修改
    /// </summary>
    [Fact]
    public async Task IsRecordApprovedAsync_WithApprovedDistribution_ReturnsTrue()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id, isApproved: true);
        var service = new WorkflowService(context);

        // Act
        var isApproved = await service.IsRecordApprovedAsync(
            ApprovalRequestType.ReportModification, distribution.Id);

        // Assert
        Assert.True(isApproved);
    }

    /// <summary>
    /// 测试未审批的上报记录可以直接修改
    /// </summary>
    [Fact]
    public async Task IsRecordApprovedAsync_WithUnapprovedDistribution_ReturnsFalse()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id, isApproved: false);
        var service = new WorkflowService(context);

        // Act
        var isApproved = await service.IsRecordApprovedAsync(
            ApprovalRequestType.ReportModification, distribution.Id);

        // Assert
        Assert.False(isApproved);
    }

    /// <summary>
    /// 测试检查是否有待处理的修改请求
    /// </summary>
    [Fact]
    public async Task HasPendingModificationRequestAsync_WithPendingRequest_ReturnsTrue()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{}",
            "{}");

        // Act
        var hasPending = await service.HasPendingModificationRequestAsync(
            ApprovalRequestType.ReportModification, distribution.Id);

        // Assert
        Assert.True(hasPending);
    }

    /// <summary>
    /// 测试没有待处理的修改请求
    /// </summary>
    [Fact]
    public async Task HasPendingModificationRequestAsync_WithoutPendingRequest_ReturnsFalse()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        // Act
        var hasPending = await service.HasPendingModificationRequestAsync(
            ApprovalRequestType.ReportModification, distribution.Id);

        // Assert
        Assert.False(hasPending);
    }

    /// <summary>
    /// 测试审批通过后没有待处理请求
    /// </summary>
    [Fact]
    public async Task HasPendingModificationRequestAsync_AfterApproval_ReturnsFalse()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        var request = await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{\"ActualCompletion\": 30}",
            "{\"ActualCompletion\": 35}");

        await service.ApproveAsync(request.Id, divisionUser.Id, "同意");

        // Act
        var hasPending = await service.HasPendingModificationRequestAsync(
            ApprovalRequestType.ReportModification, distribution.Id);

        // Assert
        Assert.False(hasPending);
    }

    /// <summary>
    /// 测试只能审批待处理的请求
    /// </summary>
    [Fact]
    public async Task ApproveAsync_WithAlreadyApprovedRequest_ThrowsInvalidOperationException()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        var request = await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{\"ActualCompletion\": 30}",
            "{\"ActualCompletion\": 35}");

        await service.ApproveAsync(request.Id, divisionUser.Id, "第一次审批");

        // Act & Assert
        var exception = await Assert.ThrowsAsync<InvalidOperationException>(() =>
            service.ApproveAsync(request.Id, divisionUser.Id, "第二次审批"));

        Assert.Equal("只能审批待处理的请求", exception.Message);
    }

    /// <summary>
    /// 测试只能拒绝待处理的请求
    /// </summary>
    [Fact]
    public async Task RejectAsync_WithAlreadyRejectedRequest_ThrowsInvalidOperationException()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        var request = await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{}",
            "{}");

        await service.RejectAsync(request.Id, divisionUser.Id, "第一次拒绝");

        // Act & Assert
        var exception = await Assert.ThrowsAsync<InvalidOperationException>(() =>
            service.RejectAsync(request.Id, divisionUser.Id, "第二次拒绝"));

        Assert.Equal("只能拒绝待处理的请求", exception.Message);
    }

    #endregion


    #region 查询测试

    /// <summary>
    /// 测试获取待处理请求
    /// </summary>
    [Fact]
    public async Task GetPendingRequestsAsync_ReturnsPendingRequests()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{}",
            "{}");

        // Act
        var pendingRequests = await service.GetPendingRequestsAsync();

        // Assert
        Assert.Single(pendingRequests);
        Assert.All(pendingRequests, r => Assert.Equal(ApprovalStatus.Pending, r.Status));
    }

    /// <summary>
    /// 测试根据ID获取请求
    /// </summary>
    [Fact]
    public async Task GetByIdAsync_WithExistingRequest_ReturnsRequest()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        var created = await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{}",
            "{}");

        // Act
        var request = await service.GetByIdAsync(created.Id);

        // Assert
        Assert.NotNull(request);
        Assert.Equal(created.Id, request.Id);
    }

    /// <summary>
    /// 测试根据ID获取不存在的请求
    /// </summary>
    [Fact]
    public async Task GetByIdAsync_WithNonExistingRequest_ReturnsNull()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var service = new WorkflowService(context);

        // Act
        var request = await service.GetByIdAsync(999);

        // Assert
        Assert.Null(request);
    }

    /// <summary>
    /// 测试根据单位获取请求
    /// </summary>
    [Fact]
    public async Task GetRequestsByUnitAsync_ReturnsUnitRequests()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{}",
            "{}");

        // Act
        var requests = await service.GetRequestsByUnitAsync(regiment.Id);

        // Assert
        Assert.Single(requests);
        Assert.All(requests, r => Assert.Equal(regiment.Id, r.RequestedByUnitId));
    }

    /// <summary>
    /// 测试根据类型获取请求
    /// </summary>
    [Fact]
    public async Task GetRequestsByTypeAsync_ReturnsTypedRequests()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (division, regiment, divisionUser, regimentUser) = CreateTestData(context);
        var allocation = CreateAllocation(context, division.Id);
        var distribution = CreateDistribution(context, allocation.Id, regiment.Id);
        var service = new WorkflowService(context);

        await service.CreateModificationRequestAsync(
            ApprovalRequestType.ReportModification,
            distribution.Id,
            regimentUser.Id,
            regiment.Id,
            "测试原因",
            "{}",
            "{}");

        // Act
        var requests = await service.GetRequestsByTypeAsync(ApprovalRequestType.ReportModification);

        // Assert
        Assert.Single(requests);
        Assert.All(requests, r => Assert.Equal(ApprovalRequestType.ReportModification, r.Type));
    }

    #endregion
}
