using ERPCore.Domain.Entities;
using ERPCore.Domain.Enums;
using ERPCore.Dtos.Common;
using ERPCore.Dtos.Dashboard;
using ERPCore.Repositories.Interfaces;
using ERPCore.Services.Interfaces;
using Microsoft.EntityFrameworkCore;

namespace ERPCore.Services;

/// <summary>
/// Aggregate counts pulled straight from each domain's repository — no PagedResponse
/// overhead, since the dashboard only needs totals. Low-stock reuses <see cref="IReorderService"/>
/// rather than re-deriving the FIFO-available-vs-reorder-point comparison (docs/11 §5.7).
/// On-hand summary and stock valuation sum <see cref="StockLayer.QtyRemaining"/> (and
/// QtyRemaining × UnitCost for valuation) directly in SQL — cheap, unlike reorder alerts,
/// because they need no per-item live lookup.
/// </summary>
public sealed class DashboardService : IDashboardService
{
    private readonly IRepository<StockLayer> _layers;
    private readonly IRepository<Grn> _grns;
    private readonly IRepository<PurchaseOrder> _pos;
    private readonly IRepository<Requisition> _requisitions;
    private readonly IRepository<StockCount> _counts;
    private readonly IRepository<Rfq> _rfqs;
    private readonly IReorderService _reorder;

    public DashboardService(
        IRepository<StockLayer> layers, IRepository<Grn> grns, IRepository<PurchaseOrder> pos,
        IRepository<Requisition> requisitions, IRepository<StockCount> counts, IRepository<Rfq> rfqs,
        IReorderService reorder)
    {
        _layers = layers;
        _grns = grns;
        _pos = pos;
        _requisitions = requisitions;
        _counts = counts;
        _rfqs = rfqs;
        _reorder = reorder;
    }

    public async Task<DashboardStatsDto> GetStatsAsync(CancellationToken ct = default)
    {
        // Sequential, not Task.WhenAll: these repositories share one scoped DbContext,
        // which cannot run concurrent operations.
        var onHandTotal = await _layers.Query().AsNoTracking().SumAsync(l => (decimal?)l.QtyRemaining, ct) ?? 0m;
        var onHandWarehouses = await _layers.Query().AsNoTracking()
            .Select(l => l.WarehouseId).Distinct().CountAsync(ct);
        var stockValuationTotal = await _layers.Query().AsNoTracking()
            .SumAsync(l => (decimal?)(l.QtyRemaining * l.UnitCost), ct) ?? 0m;
        // EF can't translate constructing WarehouseValuationDto directly inside the GroupBy
        // Select — project to an anonymous type first, then materialize into the record.
        var stockValuationByWarehouseRaw = await _layers.Query().AsNoTracking()
            .GroupBy(l => l.WarehouseId)
            .Select(g => new { WarehouseId = g.Key, Total = g.Sum(x => x.QtyRemaining * x.UnitCost) })
            .OrderByDescending(w => w.Total)
            .ToListAsync(ct);
        var stockValuationByWarehouse = stockValuationByWarehouseRaw
            .Select(w => new WarehouseValuationDto(w.WarehouseId, w.Total))
            .ToList();
        var pendingGrns = await _grns.Query().AsNoTracking().CountAsync(g => g.Status == GrnStatus.Draft, ct);
        var pendingApprovalPOs = await _pos.Query().AsNoTracking()
            .CountAsync(p => p.Status == PurchaseOrderStatus.PendingApproval, ct);
        var openRequisitions = await _requisitions.Query().AsNoTracking()
            .CountAsync(r => r.Status == RequisitionStatus.Submitted, ct);
        var pendingCounts = await _counts.Query().AsNoTracking()
            .CountAsync(c => c.Status == CountStatus.Counted, ct);
        var openRfqs = await _rfqs.Query().AsNoTracking().CountAsync(r => r.Status == RfqStatus.Open, ct);

        // PageSize:1 is enough — GetAlertsAsync computes the full alert count before paging.
        var lowStockAlerts = (await _reorder.GetAlertsAsync(null, new PageQuery { Page = 1, PageSize = 1 }, ct))
            .Pagination.TotalItems;

        return new DashboardStatsDto(
            lowStockAlerts, onHandTotal, onHandWarehouses, stockValuationTotal, stockValuationByWarehouse,
            pendingApprovalPOs, pendingGrns, openRequisitions, pendingCounts, openRfqs);
    }
}
