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

namespace MilitaryTrainingManagement.Tests.Services;

/// <summary>
/// 上报服务单元测试
/// 测试上报数据提交、数据汇总算法和权限控制
/// _需求：3.1, 3.2, 3.3, 3.4, 3.5, 4.1, 4.2, 4.3_
/// </summary>
public class ReportingServiceTests
{
    private static ApplicationDbContext CreateInMemoryContext(string dbName)
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(databaseName: dbName)
            .Options;
        return new ApplicationDbContext(options);
    }

    private static (Dictionary<OrganizationalLevel, OrganizationalUnit> hierarchy, UserAccount user) 
        CreateHierarchyWithUser(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 battalion = new OrganizationalUnit
        {
            Name = "营",
            Level = OrganizationalLevel.Battalion,
            ParentId = regiment.Id,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(battalion);
        context.SaveChanges();

        var company = new OrganizationalUnit
        {
            Name = "连",
            Level = OrganizationalLevel.Company,
            ParentId = battalion.Id,
            CreatedAt = DateTime.UtcNow
        };
        context.OrganizationalUnits.Add(company);
        context.SaveChanges();

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

        return (new Dictionary<OrganizationalLevel, OrganizationalUnit>
        {
            { OrganizationalLevel.Division, division },
            { OrganizationalLevel.Regiment, regiment },
            { OrganizationalLevel.Battalion, battalion },
            { OrganizationalLevel.Company, company }
        }, user);
    }

    private static AllocationDistribution CreateDistribution(
        ApplicationDbContext context,
        OrganizationalUnit createdByUnit,
        OrganizationalUnit targetUnit,
        decimal quota)
    {
        var allocation = new MaterialAllocation
        {
            Category = "弹药",
            MaterialName = "测试物资",
            Unit = "发",
            TotalQuota = quota * 2,
            CreatedByUnitId = createdByUnit.Id,
            CreatedAt = DateTime.UtcNow
        };
        context.MaterialAllocations.Add(allocation);
        context.SaveChanges();

        var distribution = new AllocationDistribution
        {
            AllocationId = allocation.Id,
            TargetUnitId = targetUnit.Id,
            UnitQuota = quota
        };
        context.AllocationDistributions.Add(distribution);
        context.SaveChanges();

        return distribution;
    }

    #region 上报数据提交测试 (需求3.1, 3.2)

    /// <summary>
    /// 测试提交上报数据成功
    /// </summary>
    [Fact]
    public async Task SubmitReportAsync_WithValidData_SubmitsReport()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, user) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        var distribution = CreateDistribution(
            context,
            hierarchy[OrganizationalLevel.Division],
            hierarchy[OrganizationalLevel.Regiment],
            100m);

        // Act
        var result = await service.SubmitReportAsync(distribution.Id, 80m, user.Id);

        // Assert
        Assert.NotNull(result);
        Assert.Equal(80m, result.ActualCompletion);
        Assert.NotNull(result.ReportedAt);
        Assert.Equal(user.Id, result.ReportedByUserId);
    }

    /// <summary>
    /// 测试提交上报数据 - 分配记录不存在
    /// </summary>
    [Fact]
    public async Task SubmitReportAsync_WithNonExistingDistribution_ThrowsArgumentException()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (_, user) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        // Act & Assert
        var exception = await Assert.ThrowsAsync<ArgumentException>(() =>
            service.SubmitReportAsync(999, 80m, user.Id));

        Assert.Equal("分配记录不存在", exception.Message);
    }

    /// <summary>
    /// 测试提交上报数据 - 负数实际完成
    /// </summary>
    [Fact]
    public async Task SubmitReportAsync_WithNegativeCompletion_ThrowsArgumentException()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, user) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        var distribution = CreateDistribution(
            context,
            hierarchy[OrganizationalLevel.Division],
            hierarchy[OrganizationalLevel.Regiment],
            100m);

        // Act & Assert
        var exception = await Assert.ThrowsAsync<ArgumentException>(() =>
            service.SubmitReportAsync(distribution.Id, -10m, user.Id));

        Assert.Equal("实际完成数量不能为负数", exception.Message);
    }

    #endregion

    #region 完成率计算测试 (需求3.3)

    /// <summary>
    /// 测试完成率计算 - 正常情况
    /// </summary>
    [Fact]
    public void CalculateCompletionRate_WithValidData_ReturnsCorrectRate()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        // Act
        var rate = service.CalculateCompletionRate(80m, 100m);

        // Assert
        Assert.Equal(0.8m, rate);
    }

    /// <summary>
    /// 测试完成率计算 - 零配额
    /// </summary>
    [Fact]
    public void CalculateCompletionRate_WithZeroQuota_ReturnsZero()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        // Act
        var rate = service.CalculateCompletionRate(80m, 0m);

        // Assert
        Assert.Equal(0m, rate);
    }

    /// <summary>
    /// 测试完成率计算 - 无实际完成
    /// </summary>
    [Fact]
    public void CalculateCompletionRate_WithNullCompletion_ReturnsZero()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        // Act
        var rate = service.CalculateCompletionRate(null, 100m);

        // Assert
        Assert.Equal(0m, rate);
    }

    #endregion

    #region 数据汇总测试 (需求3.4, 4.1, 4.2)

    /// <summary>
    /// 测试获取汇总数据 - 包含下级单位
    /// </summary>
    [Fact]
    public async Task GetAggregatedDataAsync_IncludesSubordinateUnits()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, _) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        // 为团和营创建分配
        CreateDistribution(context, hierarchy[OrganizationalLevel.Division], 
            hierarchy[OrganizationalLevel.Regiment], 100m);
        CreateDistribution(context, hierarchy[OrganizationalLevel.Division], 
            hierarchy[OrganizationalLevel.Battalion], 50m);

        // Act - 使用师团级别，应该能看到所有下级
        var result = await service.GetAggregatedDataAsync(hierarchy[OrganizationalLevel.Division].Id, OrganizationalLevel.Division);

        // Assert
        Assert.Equal(2, result.Count());
    }

    /// <summary>
    /// 测试获取层级汇总数据
    /// </summary>
    [Fact]
    public async Task GetHierarchicalAggregatedDataAsync_ReturnsCorrectAggregation()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, user) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        // 创建分配并提交上报
        var distribution1 = CreateDistribution(context, hierarchy[OrganizationalLevel.Division], 
            hierarchy[OrganizationalLevel.Regiment], 100m);
        var distribution2 = CreateDistribution(context, hierarchy[OrganizationalLevel.Division], 
            hierarchy[OrganizationalLevel.Battalion], 50m);

        await service.SubmitReportAsync(distribution1.Id, 80m, user.Id);
        await service.SubmitReportAsync(distribution2.Id, 40m, user.Id);

        // Act
        var result = await service.GetHierarchicalAggregatedDataAsync(hierarchy[OrganizationalLevel.Division].Id);

        // Assert
        Assert.Equal(150m, result.TotalQuota);
        Assert.Equal(120m, result.TotalActualCompletion);
        Assert.Equal(0.8m, result.OverallCompletionRate);
    }

    /// <summary>
    /// 测试获取层级汇总数据 - 组织单位不存在
    /// </summary>
    [Fact]
    public async Task GetHierarchicalAggregatedDataAsync_WithNonExistingUnit_ThrowsArgumentException()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        // Act & Assert
        var exception = await Assert.ThrowsAsync<ArgumentException>(() =>
            service.GetHierarchicalAggregatedDataAsync(999));

        Assert.Equal("组织单位不存在", exception.Message);
    }

    #endregion

    #region 权限控制测试 (需求4.4, 8.1, 8.2)

    /// <summary>
    /// 测试权限检查 - 同一单位
    /// </summary>
    [Fact]
    public async Task CanViewUnitDataAsync_SameUnit_ReturnsTrue()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, _) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        var unitId = hierarchy[OrganizationalLevel.Regiment].Id;

        // Act
        var canView = await service.CanViewUnitDataAsync(unitId, unitId, OrganizationalLevel.Regiment);

        // Assert
        Assert.True(canView);
    }

    /// <summary>
    /// 测试权限检查 - 上级查看下级
    /// </summary>
    [Fact]
    public async Task CanViewUnitDataAsync_SuperiorViewsSubordinate_ReturnsTrue()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, _) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        var divisionId = hierarchy[OrganizationalLevel.Division].Id;
        var regimentId = hierarchy[OrganizationalLevel.Regiment].Id;

        // Act - 师团级别可以查看所有下级
        var canView = await service.CanViewUnitDataAsync(divisionId, regimentId, OrganizationalLevel.Division);

        // Assert
        Assert.True(canView);
    }

    /// <summary>
    /// 测试权限检查 - 下级查看上级
    /// </summary>
    [Fact]
    public async Task CanViewUnitDataAsync_SubordinateViewsSuperior_ReturnsFalse()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, _) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        var divisionId = hierarchy[OrganizationalLevel.Division].Id;
        var regimentId = hierarchy[OrganizationalLevel.Regiment].Id;

        // Act - 团级不能查看师团级
        var canView = await service.CanViewUnitDataAsync(regimentId, divisionId, OrganizationalLevel.Regiment);

        // Assert
        Assert.False(canView);
    }

    /// <summary>
    /// 测试权限检查 - 跨层级查看（非直接上下级）
    /// </summary>
    [Fact]
    public async Task CanViewUnitDataAsync_DivisionViewsCompany_ReturnsTrue()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, _) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        var divisionId = hierarchy[OrganizationalLevel.Division].Id;
        var companyId = hierarchy[OrganizationalLevel.Company].Id;

        // Act - 师团级别可以查看所有下级（包括连级）
        var canView = await service.CanViewUnitDataAsync(divisionId, companyId, OrganizationalLevel.Division);

        // Assert
        Assert.True(canView);
    }

    #endregion

    #region 详细上报记录测试 (需求4.3)

    /// <summary>
    /// 测试获取详细上报记录
    /// </summary>
    [Fact]
    public async Task GetDetailedReportsAsync_ReturnsReportsWithTimestampsAndReporter()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, user) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        var distribution = CreateDistribution(context, hierarchy[OrganizationalLevel.Division], 
            hierarchy[OrganizationalLevel.Regiment], 100m);
        await service.SubmitReportAsync(distribution.Id, 80m, user.Id);

        // Act - 使用师团级别，应该能看到所有下级
        var reports = await service.GetDetailedReportsAsync(hierarchy[OrganizationalLevel.Division].Id, OrganizationalLevel.Division);

        // Assert
        var report = reports.First();
        Assert.NotNull(report.ReportedAt);
        Assert.Equal(user.Id, report.ReportedByUserId);
        Assert.Equal(user.DisplayName, report.ReportedByUserName);
    }

    /// <summary>
    /// 测试获取详细上报记录 - 包含物资信息
    /// </summary>
    [Fact]
    public async Task GetDetailedReportsAsync_IncludesMaterialInfo()
    {
        // Arrange
        var dbName = Guid.NewGuid().ToString();
        using var context = CreateInMemoryContext(dbName);
        var (hierarchy, user) = CreateHierarchyWithUser(context);
        var organizationService = new OrganizationService(context);
        var service = new ReportingService(context, organizationService);

        var distribution = CreateDistribution(context, hierarchy[OrganizationalLevel.Division], 
            hierarchy[OrganizationalLevel.Regiment], 100m);
        await service.SubmitReportAsync(distribution.Id, 80m, user.Id);

        // Act - 使用师团级别，应该能看到所有下级
        var reports = await service.GetDetailedReportsAsync(hierarchy[OrganizationalLevel.Division].Id, OrganizationalLevel.Division);

        // Assert
        var report = reports.First();
        Assert.Equal("弹药", report.Category);
        Assert.Equal("测试物资", report.MaterialName);
        Assert.Equal("发", report.Unit);
        Assert.Equal(100m, report.UnitQuota);
        Assert.Equal(80m, report.ActualCompletion);
        Assert.Equal(0.8m, report.CompletionRate);
    }

    #endregion
}
