using MediatR;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Domain.Contracts;
using SolutionErp.Domain.Identity;

namespace SolutionErp.Application.Reports.Queries.GetMyDashboard;

// Per-user dashboard stats — role-aware. Drafter sees their own drafts;
// approvers see what's waiting on their desk; everyone sees SLA pressure
// relevant to them.
public record GetMyDashboardQuery : IRequest<MyDashboardDto>;

public record MyDashboardDto(
    int DraftsInProgress,   // HĐ tôi là Drafter + đang ở DangSoanThao/DangGopY/DangDamPhan
    int PendingMyApproval,  // HĐ ở phase mà role tôi eligible advance (không đếm HĐ mình là Drafter)
    int DueSoon,            // Trong 24h tới (intersect với 2 ở trên)
    int Overdue,            // Đã quá hạn (intersect)
    decimal DraftsTotalValue);

public class GetMyDashboardQueryHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser,
    IDateTime clock) : IRequestHandler<GetMyDashboardQuery, MyDashboardDto>
{
    // Same map as ContractFeatures.GetMyInboxQuery — roles eligible for each phase
    private static readonly Dictionary<ContractPhase, string[]> PhaseActorRoles = new()
    {
        [ContractPhase.DangSoanThao] = [AppRoles.Drafter, AppRoles.DeptManager],
        [ContractPhase.DangGopY] = [AppRoles.ProjectManager, AppRoles.Procurement, AppRoles.CostControl, AppRoles.Finance, AppRoles.Accounting, AppRoles.Equipment],
        [ContractPhase.DangDamPhan] = [AppRoles.Drafter, AppRoles.DeptManager, AppRoles.ProjectManager],
        [ContractPhase.DangInKy] = [AppRoles.Drafter, AppRoles.DeptManager, AppRoles.ProjectManager],
        [ContractPhase.DangKiemTraCCM] = [AppRoles.CostControl],
        [ContractPhase.DangTrinhKy] = [AppRoles.Director, AppRoles.AuthorizedSigner],
        [ContractPhase.DangDongDau] = [AppRoles.HrAdmin],
    };

    public async Task<MyDashboardDto> Handle(GetMyDashboardQuery request, CancellationToken ct)
    {
        var userId = currentUser.UserId ?? throw new UnauthorizedException();
        var userRoles = currentUser.Roles;
        var now = clock.UtcNow;
        var dayFromNow = now.AddHours(24);

        var eligiblePhases = PhaseActorRoles
            .Where(kv => kv.Value.Any(r => userRoles.Contains(r)))
            .Select(kv => kv.Key)
            .ToList();

        var contracts = db.Contracts.AsNoTracking()
            .Where(c => c.Phase != ContractPhase.DaPhatHanh && c.Phase != ContractPhase.TuChoi);

        // Drafts I'm authoring — I'm the Drafter + phase is pre-signing
        var inProgressPhases = new[] {
            ContractPhase.DangSoanThao, ContractPhase.DangGopY, ContractPhase.DangDamPhan,
        };
        var myDrafts = contracts.Where(c => c.DrafterUserId == userId && inProgressPhases.Contains(c.Phase));

        var draftsCount = await myDrafts.CountAsync(ct);
        var draftsValue = await myDrafts.SumAsync(c => (decimal?)c.GiaTri, ct) ?? 0m;

        // Pending my approval — phase is in my eligibility + I'm NOT the drafter
        // (drafters shouldn't self-approve). Empty if user has no relevant role.
        var pendingMine = eligiblePhases.Count == 0
            ? contracts.Where(c => false)
            : contracts.Where(c => eligiblePhases.Contains(c.Phase) && c.DrafterUserId != userId);

        var pendingCount = await pendingMine.CountAsync(ct);

        // SLA pressure across both sets — a contract I'm drafting + near deadline,
        // OR one I should approve + near deadline.
        var mine = myDrafts.Union(pendingMine);
        var dueSoon = await mine.CountAsync(c => c.SlaDeadline != null && c.SlaDeadline >= now && c.SlaDeadline < dayFromNow, ct);
        var overdue = await mine.CountAsync(c => c.SlaDeadline != null && c.SlaDeadline < now, ct);

        return new MyDashboardDto(draftsCount, pendingCount, dueSoon, overdue, draftsValue);
    }
}
