using Microsoft.EntityFrameworkCore;
using MilitaryTrainingManagement.Data;
using MilitaryTrainingManagement.Models.Entities;
using MilitaryTrainingManagement.Models.DTOs;
using MilitaryTrainingManagement.Services.Interfaces;

namespace MilitaryTrainingManagement.Services.Implementations;

/// <summary>
/// 物资配额服务实现
/// </summary>
public class AllocationService : IAllocationService
{
    private readonly ApplicationDbContext _context;
    private readonly IOrganizationService _organizationService;

    public AllocationService(ApplicationDbContext context, IOrganizationService organizationService)
    {
        _context = context;
        _organizationService = organizationService;
    }

    public async Task<MaterialAllocation?> GetByIdAsync(int id)
    {
        return await _context.MaterialAllocations
            .Include(a => a.CreatedByUnit)
            .Include(a => a.Distributions)
                .ThenInclude(d => d.TargetUnit)
            .Include(a => a.Distributions)
                .ThenInclude(d => d.ReportedByUser)
            .FirstOrDefaultAsync(a => a.Id == id);
    }

    public async Task<IEnumerable<MaterialAllocation>> GetAllAsync()
    {
        return await _context.MaterialAllocations
            .Include(a => a.CreatedByUnit)
            .Include(a => a.Distributions)
                .ThenInclude(d => d.TargetUnit)
            .OrderByDescending(a => a.CreatedAt)
            .ToListAsync();
    }

    public async Task<IEnumerable<MaterialAllocation>> GetByUnitAsync(int unitId)
    {
        return await _context.MaterialAllocations
            .Include(a => a.CreatedByUnit)
            .Include(a => a.Distributions)
                .ThenInclude(d => d.TargetUnit)
            .Where(a => a.CreatedByUnitId == unitId)
            .OrderByDescending(a => a.CreatedAt)
            .ToListAsync();
    }

    public async Task<IEnumerable<MaterialAllocation>> GetVisibleToUnitAsync(int unitId)
    {
        // 获取该单位及其所有下级单位的ID
        var subordinateIds = await _organizationService.GetAllSubordinateIdsAsync(unitId);
        var allUnitIds = new HashSet<int>(subordinateIds) { unitId };

        // 获取该单位的所有上级单位ID（用于营部及以下账号查看团的配额）
        var ancestorIds = await GetAncestorUnitIdsAsync(unitId);
        var allRelatedUnitIds = new HashSet<int>(allUnitIds);
        foreach (var ancestorId in ancestorIds)
        {
            allRelatedUnitIds.Add(ancestorId);
        }

        // 获取分配给这些单位的配额
        var allocations = await _context.MaterialAllocations
            .Include(a => a.CreatedByUnit)
            .Include(a => a.Distributions)
                .ThenInclude(d => d.TargetUnit)
            .Include(a => a.Distributions)
                .ThenInclude(d => d.ReportedByUser)
            .Where(a => a.Distributions.Any(d => allRelatedUnitIds.Contains(d.TargetUnitId)))
            .OrderByDescending(a => a.CreatedAt)
            .ToListAsync();

        // 过滤每个配额的分配记录,只保留分配给当前单位及其上下级的记录
        foreach (var allocation in allocations)
        {
            allocation.Distributions = allocation.Distributions
                .Where(d => allRelatedUnitIds.Contains(d.TargetUnitId))
                .ToList();
        }

        return allocations;
    }

    /// <summary>
    /// 获取单位的所有上级单位ID
    /// </summary>
    private async Task<List<int>> GetAncestorUnitIdsAsync(int unitId)
    {
        var result = new List<int>();
        var currentUnit = await _context.OrganizationalUnits.FindAsync(unitId);
        
        while (currentUnit?.ParentId != null)
        {
            result.Add(currentUnit.ParentId.Value);
            currentUnit = await _context.OrganizationalUnits.FindAsync(currentUnit.ParentId.Value);
        }
        
        return result;
    }

    public async Task<IEnumerable<AllocationDistribution>> GetDistributionsForUnitAsync(int unitId)
    {
        return await _context.AllocationDistributions
            .Include(d => d.Allocation)
                .ThenInclude(a => a.CreatedByUnit)
            .Include(d => d.TargetUnit)
            .Include(d => d.ReportedByUser)
            .Where(d => d.TargetUnitId == unitId)
            .OrderByDescending(d => d.Allocation.CreatedAt)
            .ToListAsync();
    }

    public async Task<AllocationDistribution?> GetDistributionByIdAsync(int id)
    {
        return await _context.AllocationDistributions
            .Include(d => d.Allocation)
                .ThenInclude(a => a.CreatedByUnit)
            .Include(d => d.TargetUnit)
            .Include(d => d.ReportedByUser)
            .FirstOrDefaultAsync(d => d.Id == id);
    }

    public async Task<MaterialAllocation> CreateAsync(
        string category, 
        string materialName, 
        string unit, 
        decimal totalQuota, 
        int createdByUnitId, 
        Dictionary<int, decimal> distributions)
    {
        // 验证输入
        if (string.IsNullOrWhiteSpace(category))
            throw new ArgumentException("类别不能为空");
        if (string.IsNullOrWhiteSpace(materialName))
            throw new ArgumentException("物资名称不能为空");
        if (string.IsNullOrWhiteSpace(unit))
            throw new ArgumentException("计量单位不能为空");
        if (totalQuota <= 0)
            throw new ArgumentException("总配额必须大于0");

        // 验证分配配额
        if (distributions.Values.Any(q => q < 0))
            throw new ArgumentException("单位配额不能为负数");

        if (!await ValidateDistributionQuotasAsync(totalQuota, distributions))
            throw new ArgumentException("有配额未分配，无法提交");

        // 验证目标单位存在
        if (!await ValidateTargetUnitsExistAsync(distributions.Keys))
            throw new ArgumentException("目标单位不存在");

        // 创建物资配额
        var allocation = new MaterialAllocation
        {
            Category = category,
            MaterialName = materialName,
            Unit = unit,
            TotalQuota = totalQuota,
            CreatedByUnitId = createdByUnitId,
            CreatedAt = DateTime.Now
        };

        _context.MaterialAllocations.Add(allocation);
        await _context.SaveChangesAsync();

        // 自动创建分配记录（需求2.4：自动分发给目标单位）
        foreach (var (targetUnitId, quota) in distributions)
        {
            var distribution = new AllocationDistribution
            {
                AllocationId = allocation.Id,
                TargetUnitId = targetUnitId,
                UnitQuota = quota
            };
            _context.AllocationDistributions.Add(distribution);
        }

        await _context.SaveChangesAsync();

        return await GetByIdAsync(allocation.Id) ?? allocation;
    }

    public async Task<MaterialAllocation> UpdateAsync(
        int id, 
        string category, 
        string materialName, 
        string unit, 
        decimal totalQuota)
    {
        var allocation = await _context.MaterialAllocations
            .Include(a => a.Distributions)
            .FirstOrDefaultAsync(a => a.Id == id);

        if (allocation == null)
            throw new ArgumentException("配额不存在");

        // 验证输入
        if (string.IsNullOrWhiteSpace(category))
            throw new ArgumentException("类别不能为空");
        if (string.IsNullOrWhiteSpace(materialName))
            throw new ArgumentException("物资名称不能为空");
        if (string.IsNullOrWhiteSpace(unit))
            throw new ArgumentException("计量单位不能为空");
        if (totalQuota <= 0)
            throw new ArgumentException("总配额必须大于0");

        // 验证新的总配额不小于已分配的配额总和
        var currentDistributionSum = allocation.Distributions.Sum(d => d.UnitQuota);
        if (totalQuota < currentDistributionSum)
            throw new ArgumentException($"总配额不能小于已分配的配额总和({currentDistributionSum})");

        allocation.Category = category;
        allocation.MaterialName = materialName;
        allocation.Unit = unit;
        allocation.TotalQuota = totalQuota;

        await _context.SaveChangesAsync();
        return allocation;
    }

    public async Task<MaterialAllocation> UpdateDistributionsAsync(
        int allocationId, 
        Dictionary<int, decimal> distributions)
    {
        var allocation = await _context.MaterialAllocations
            .Include(a => a.Distributions)
            .FirstOrDefaultAsync(a => a.Id == allocationId);

        if (allocation == null)
            throw new ArgumentException("配额不存在");

        // 验证分配配额
        if (distributions.Values.Any(q => q < 0))
            throw new ArgumentException("单位配额不能为负数");

        if (!await ValidateDistributionQuotasAsync(allocation.TotalQuota, distributions))
            throw new ArgumentException("有配额未分配，无法提交");

        // 验证目标单位存在
        if (!await ValidateTargetUnitsExistAsync(distributions.Keys))
            throw new ArgumentException("目标单位不存在");

        // 删除现有分配记录
        _context.AllocationDistributions.RemoveRange(allocation.Distributions);

        // 创建新的分配记录
        foreach (var (targetUnitId, quota) in distributions)
        {
            var distribution = new AllocationDistribution
            {
                AllocationId = allocationId,
                TargetUnitId = targetUnitId,
                UnitQuota = quota
            };
            _context.AllocationDistributions.Add(distribution);
        }

        await _context.SaveChangesAsync();

        return await GetByIdAsync(allocationId) ?? allocation;
    }

    public async Task<AllocationDistribution> UpdateDistributionCompletionAsync(
        int distributionId, 
        int unitId, 
        int userId,
        decimal actualCompletion,
        string? remarks = null)
    {
        var distribution = await _context.AllocationDistributions
            .Include(d => d.Allocation)
            .Include(d => d.TargetUnit)
            .Include(d => d.ReportedByUser)
            .FirstOrDefaultAsync(d => d.Id == distributionId);

        if (distribution == null)
            throw new ArgumentException("配额分配记录不存在");

        // 验证权限：可以更新分配给自己单位或上级单位的记录
        var canReport = distribution.TargetUnitId == unitId;
        if (!canReport)
        {
            // 检查是否是上级单位的配额
            var ancestorIds = await GetAncestorUnitIdsAsync(unitId);
            canReport = ancestorIds.Contains(distribution.TargetUnitId);
        }
        
        if (!canReport)
            throw new UnauthorizedAccessException("无权更新此配额分配记录");

        // 验证实际完成数量
        if (actualCompletion < 0)
            throw new ArgumentException("实际完成数量不能为负数");

        if (actualCompletion > distribution.UnitQuota)
            throw new ArgumentException($"实际完成数量不能超过分配配额({distribution.UnitQuota})");

        // 计算本次上报数量
        var previousAmount = distribution.ActualCompletion ?? 0;
        var reportedAmount = actualCompletion - previousAmount;

        // 如果本次上报数量大于0，保存历史记录
        if (reportedAmount > 0)
        {
            var consumptionReport = new ConsumptionReport
            {
                AllocationDistributionId = distributionId,
                ReportedAmount = reportedAmount,
                CumulativeAmount = actualCompletion,
                Remarks = remarks,
                ReportedByUserId = userId,
                ReportedByUnitId = unitId,
                ReportedAt = DateTime.UtcNow
            };
            _context.ConsumptionReports.Add(consumptionReport);
        }

        // 更新实际完成数量
        distribution.ActualCompletion = actualCompletion;
        distribution.ReportedAt = DateTime.UtcNow;
        distribution.ReportedByUserId = userId;

        await _context.SaveChangesAsync();

        return distribution;
    }

    public async Task DeleteAsync(int id)
    {
        var allocation = await _context.MaterialAllocations.FindAsync(id);
        if (allocation == null)
            throw new ArgumentException("配额不存在");

        _context.MaterialAllocations.Remove(allocation);
        await _context.SaveChangesAsync();
    }

    public Task<bool> ValidateDistributionQuotasAsync(decimal totalQuota, Dictionary<int, decimal> distributions)
    {
        if (distributions == null || distributions.Count == 0)
            return Task.FromResult(false); // 必须有分配

        var sum = distributions.Values.Sum();
        return Task.FromResult(sum == totalQuota); // 必须完全分配，不能多也不能少
    }

    public async Task<bool> ValidateTargetUnitsExistAsync(IEnumerable<int> targetUnitIds)
    {
        if (targetUnitIds == null || !targetUnitIds.Any())
            return true;

        var existingIds = await _context.OrganizationalUnits
            .Where(u => targetUnitIds.Contains(u.Id))
            .Select(u => u.Id)
            .ToListAsync();

        return existingIds.Count == targetUnitIds.Distinct().Count();
    }

    public async Task<IEnumerable<ConsumptionReport>> GetConsumptionReportsAsync(int distributionId)
    {
        return await _context.ConsumptionReports
            .Include(r => r.ReportedByUser)
            .Where(r => r.AllocationDistributionId == distributionId)
            .OrderByDescending(r => r.ReportedAt)
            .ToListAsync();
    }

    /// <summary>
    /// 获取配额分配的上报历史记录（带可见性过滤）
    /// 只返回用户可见范围内的单位的上报记录
    /// </summary>
    public async Task<IEnumerable<ConsumptionReport>> GetConsumptionReportsAsync(int distributionId, int userUnitId, Models.Enums.OrganizationalLevel userLevel)
    {
        // 获取用户可见的单位ID列表
        var visibleUnitIds = (await _organizationService.GetVisibleUnitIdsAsync(userUnitId, userLevel)).ToHashSet();

        // 获取所有上报记录
        var allReports = await _context.ConsumptionReports
            .Include(r => r.ReportedByUser)
                .ThenInclude(u => u.OrganizationalUnit)
            .Where(r => r.AllocationDistributionId == distributionId)
            .OrderByDescending(r => r.ReportedAt)
            .ToListAsync();

        // 过滤：只返回上报人所属单位在用户可见范围内的记录
        return allReports.Where(r => visibleUnitIds.Contains(r.ReportedByUser.OrganizationalUnitId)).ToList();
    }

    /// <summary>
    /// 检查用户是否有权限查看指定单位的配额分配记录
    /// 使用可见性过滤：营部及以下级别只能查看本单位及直接下级
    /// 但用户始终可以查看分配给自己单位或上级单位的记录
    /// </summary>
    public async Task<bool> CanViewDistributionAsync(int userUnitId, Models.Enums.OrganizationalLevel userLevel, int targetUnitId)
    {
        // 用户始终可以查看分配给自己单位的记录
        if (targetUnitId == userUnitId)
        {
            return true;
        }

        // 用户可以查看分配给上级单位的记录（因为下级可以查看上级的配额并上报消耗）
        var ancestorIds = await _organizationService.GetAllAncestorIdsAsync(userUnitId);
        if (ancestorIds.Contains(targetUnitId))
        {
            return true;
        }

        // 获取用户可见的单位ID列表（本单位 + 直接下级）
        var visibleUnitIds = await _organizationService.GetVisibleUnitIdsAsync(userUnitId, userLevel);

        // 检查目标单位是否在可见范围内
        return visibleUnitIds.Contains(targetUnitId);
    }

    /// <summary>
    /// 获取用户可见范围内的上报总和
    /// 营部及以下级别：只计算本单位及直接下级的上报
    /// </summary>
    public async Task<decimal> GetVisibleActualCompletionAsync(int allocationId, int userUnitId, Models.Enums.OrganizationalLevel userLevel)
    {
        // 获取用户可见的单位ID列表
        var visibleUnitIds = (await _organizationService.GetVisibleUnitIdsAsync(userUnitId, userLevel)).ToHashSet();

        // 获取该配额的所有上报记录
        var allocation = await _context.MaterialAllocations
            .Include(a => a.Distributions)
            .FirstOrDefaultAsync(a => a.Id == allocationId);

        if (allocation == null)
        {
            return 0;
        }

        // 获取所有分配记录的上报历史
        var distributionIds = allocation.Distributions.Select(d => d.Id).ToList();
        var allReports = await _context.ConsumptionReports
            .Include(r => r.ReportedByUser)
            .Where(r => distributionIds.Contains(r.AllocationDistributionId))
            .ToListAsync();

        // 只计算上报人所属单位在用户可见范围内的上报
        return allReports
            .Where(r => visibleUnitIds.Contains(r.ReportedByUser.OrganizationalUnitId))
            .Sum(r => r.ReportedAmount);
    }

    /// <summary>
    /// 获取按单位汇总的上报数据（带可见性过滤）
    /// </summary>
    public async Task<IEnumerable<UnitReportSummary>> GetReportSummaryByUnitAsync(int distributionId, int userUnitId, Models.Enums.OrganizationalLevel userLevel, DateTime? startDate = null, DateTime? endDate = null)
    {
        // 获取所有上报记录（不做可见性过滤，因为师团级需要看到所有下级的汇总）
        var query = _context.ConsumptionReports
            .Include(r => r.ReportedByUser)
                .ThenInclude(u => u.OrganizationalUnit)
            .Where(r => r.AllocationDistributionId == distributionId);

        // 按时间范围过滤
        if (startDate.HasValue)
        {
            query = query.Where(r => r.ReportedAt >= startDate.Value);
        }
        if (endDate.HasValue)
        {
            query = query.Where(r => r.ReportedAt <= endDate.Value);
        }

        var allReports = await query.ToListAsync();

        // 按单位分组汇总
        var summaries = allReports
            .GroupBy(r => new { 
                UnitId = r.ReportedByUser.OrganizationalUnitId, 
                UnitName = r.ReportedByUser.OrganizationalUnit.Name,
                UnitLevel = r.ReportedByUser.OrganizationalUnit.Level.ToString()
            })
            .Select(g => new UnitReportSummary
            {
                UnitId = g.Key.UnitId,
                UnitName = g.Key.UnitName,
                UnitLevel = g.Key.UnitLevel,
                TotalReported = g.Sum(r => r.ReportedAmount),
                ReportCount = g.Count(),
                LastReportedAt = g.Max(r => r.ReportedAt)
            })
            .OrderByDescending(s => s.TotalReported)
            .ToList();

        return summaries;
    }

    /// <summary>
    /// 获取配额的消耗统计（按时间范围）
    /// </summary>
    public async Task<IEnumerable<UnitConsumptionStat>> GetConsumptionStatsByPeriodAsync(int allocationId, DateTime startDate, DateTime endDate)
    {
        // 获取配额及其分配记录
        var allocation = await _context.MaterialAllocations
            .Include(a => a.Distributions)
                .ThenInclude(d => d.TargetUnit)
            .FirstOrDefaultAsync(a => a.Id == allocationId);

        if (allocation == null)
        {
            return Enumerable.Empty<UnitConsumptionStat>();
        }

        // 获取所有分配记录的ID
        var distributionIds = allocation.Distributions.Select(d => d.Id).ToList();

        // 获取指定时间范围内的上报记录
        var reports = await _context.ConsumptionReports
            .Include(r => r.AllocationDistribution)
                .ThenInclude(d => d.TargetUnit)
            .Where(r => distributionIds.Contains(r.AllocationDistributionId) 
                && r.ReportedAt >= startDate 
                && r.ReportedAt <= endDate)
            .ToListAsync();

        // 按目标单位（团）分组汇总
        var stats = allocation.Distributions
            .Select(d => {
                var unitReports = reports.Where(r => r.AllocationDistributionId == d.Id).ToList();
                var totalConsumed = unitReports.Sum(r => r.ReportedAmount);
                return new UnitConsumptionStat
                {
                    UnitId = d.TargetUnitId,
                    UnitName = d.TargetUnit?.Name ?? string.Empty,
                    UnitLevel = d.TargetUnit?.Level.ToString() ?? string.Empty,
                    UnitQuota = d.UnitQuota,
                    TotalConsumed = totalConsumed,
                    CompletionRate = d.UnitQuota > 0 ? totalConsumed / d.UnitQuota : 0,
                    ReportCount = unitReports.Count
                };
            })
            .OrderByDescending(s => s.TotalConsumed)
            .ToList();

        return stats;
    }
}
