using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Application.Notifications;
using SolutionErp.Application.PurchaseEvaluations.Services;
using SolutionErp.Domain.Common;
using SolutionErp.Domain.Contracts;
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Notifications;
using SolutionErp.Domain.PurchaseEvaluations;

namespace SolutionErp.Infrastructure.Services;

// Mirror ContractWorkflowService. Load policy từ pinned
// WorkflowDefinition (nếu có) hoặc fallback hardcoded registry.
public class PurchaseEvaluationWorkflowService(
    IApplicationDbContext db,
    IDateTime dateTime,
    INotificationService notifications,
    UserManager<User> userManager) : IPurchaseEvaluationWorkflowService
{
    public TimeSpan? GetPhaseSla(PurchaseEvaluationPhase phase) =>
        PurchaseEvaluationPolicies.NccOnly.PhaseSla.GetValueOrDefault(phase);

    public async Task TransitionAsync(
        PurchaseEvaluation evaluation,
        PurchaseEvaluationPhase targetPhase,
        Guid? actorUserId,
        IReadOnlyList<string> actorRoles,
        ApprovalDecision decision,
        string? comment,
        CancellationToken ct = default)
    {
        if (evaluation.Phase == targetPhase)
            throw new ConflictException("Phiếu đã ở phase đích.");

        // ===== Smart reject + resume (Phase 9 — Migration 16) =====
        var fromPhase = evaluation.Phase;
        var isResumingAfterReject = decision == ApprovalDecision.Approve
            && fromPhase == PurchaseEvaluationPhase.DangSoanThao
            && evaluation.RejectedFromPhase != null;

        if (decision == ApprovalDecision.Reject)
        {
            evaluation.RejectedFromPhase = fromPhase;
            targetPhase = PurchaseEvaluationPhase.DangSoanThao;
        }
        else if (isResumingAfterReject)
        {
            targetPhase = evaluation.RejectedFromPhase!.Value;
            evaluation.RejectedFromPhase = null;
        }

        PurchaseEvaluationPolicy policy;
        if (evaluation.WorkflowDefinitionId is Guid wfId)
        {
            var def = await db.PurchaseEvaluationWorkflowDefinitions.AsNoTracking()
                .Include(d => d.Steps.OrderBy(s => s.Order))
                    .ThenInclude(s => s.Approvers)
                .FirstOrDefaultAsync(d => d.Id == wfId, ct);
            policy = def is not null
                ? PurchaseEvaluationPolicyRegistry.FromDefinition(def)
                : PurchaseEvaluationPolicyRegistry.ForEvaluation(evaluation);
        }
        else
        {
            policy = PurchaseEvaluationPolicyRegistry.ForEvaluation(evaluation);
        }

        var isAdmin = actorRoles.Contains(AppRoles.Admin);
        var isSystem = actorUserId is null && decision == ApprovalDecision.AutoApprove;

        // Policy guard — bypass khi resume sau reject (target đã pinned).
        if (!isAdmin && !isSystem && !isResumingAfterReject)
        {
            if (!policy.Transitions.TryGetValue((fromPhase, targetPhase), out var allowedRoles))
                throw new ForbiddenException(
                    $"Policy '{policy.Name}' không cho phép {fromPhase} → {targetPhase}.");

            if (!policy.IsTransitionAllowed(fromPhase, targetPhase, actorRoles, actorUserId))
            {
                throw new ForbiddenException(
                    $"Role ({string.Join(",", actorRoles)}) không đủ quyền chuyển {fromPhase} → {targetPhase}. " +
                    $"Policy '{policy.Name}' yêu cầu: {string.Join(",", allowedRoles)}.");
            }
        }

        // ===== 2-stage department approval (Phase 9 — Migration 16) =====
        // Bug fix anh Kiệt: NV duyệt được hết phase. Logic mới:
        // - User.DepartmentId != null + KHÔNG admin/system + KHÔNG resume:
        //   - DeptManager (TPB) → Stage=Confirm trực tiếp
        //   - CanBypassReview=true → Stage=Confirm + IsBypassed=true
        //   - Else (NV) → Stage=Review only, BLOCK phase transition cho đến khi TPB confirm
        // - Skip với reject + resume + admin + system + actor không thuộc dept.
        if (decision == ApprovalDecision.Approve
            && targetPhase != PurchaseEvaluationPhase.DangSoanThao
            && targetPhase != PurchaseEvaluationPhase.TuChoi
            && !isResumingAfterReject
            && !isAdmin && !isSystem
            && actorUserId is Guid actorUid)
        {
            var actor = await userManager.FindByIdAsync(actorUid.ToString());
            if (actor?.DepartmentId is Guid deptId)
            {
                var isManager = actorRoles.Contains(AppRoles.DeptManager);
                var canBypass = actor.CanBypassReview;
                var stage = (isManager || canBypass) ? ApprovalStage.Confirm : ApprovalStage.Review;
                var isBypassed = !isManager && canBypass;
                var roleSnapshot = isManager ? "TPB" : (canBypass ? "NV(bypass)" : "NV");

                // Upsert: 1 row mỗi (PEId, phase, dept, stage). UNIQUE index enforce.
                var existing = await db.PurchaseEvaluationDepartmentApprovals
                    .FirstOrDefaultAsync(a =>
                        a.PurchaseEvaluationId == evaluation.Id
                        && a.PhaseAtApproval == (int)fromPhase
                        && a.DepartmentId == deptId
                        && a.Stage == stage, ct);
                if (existing is null)
                {
                    db.PurchaseEvaluationDepartmentApprovals.Add(new PurchaseEvaluationDepartmentApproval
                    {
                        PurchaseEvaluationId = evaluation.Id,
                        PhaseAtApproval = (int)fromPhase,
                        DepartmentId = deptId,
                        Stage = stage,
                        ApproverUserId = actorUid,
                        ApproverRoleSnapshot = roleSnapshot,
                        Comment = comment,
                        ApprovedAt = dateTime.UtcNow,
                        IsBypassed = isBypassed,
                    });
                }
                else
                {
                    existing.ApproverUserId = actorUid;
                    existing.ApproverRoleSnapshot = roleSnapshot;
                    existing.Comment = comment;
                    existing.ApprovedAt = dateTime.UtcNow;
                    existing.IsBypassed = isBypassed;
                }

                // Check Stage=Confirm tồn tại cho (PEId, fromPhase, deptId)
                var hasConfirm = stage == ApprovalStage.Confirm
                    || await db.PurchaseEvaluationDepartmentApprovals.AnyAsync(a =>
                        a.PurchaseEvaluationId == evaluation.Id
                        && a.PhaseAtApproval == (int)fromPhase
                        && a.DepartmentId == deptId
                        && a.Stage == ApprovalStage.Confirm, ct);

                if (!hasConfirm)
                {
                    // NV review xong, chưa có TPB confirm → BLOCK phase transition.
                    // Log Approval + Changelog "đã review" để audit. Phase giữ nguyên.
                    db.PurchaseEvaluationApprovals.Add(new PurchaseEvaluationApproval
                    {
                        PurchaseEvaluationId = evaluation.Id,
                        FromPhase = fromPhase,
                        ToPhase = fromPhase,    // không đổi phase
                        ApproverUserId = actorUid,
                        Decision = ApprovalDecision.Approve,
                        Comment = $"[Review NV] {comment ?? ""}",
                        ApprovedAt = dateTime.UtcNow,
                    });

                    string? reviewerName = (actor.FullName ?? actor.Email);
                    db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
                    {
                        PurchaseEvaluationId = evaluation.Id,
                        EntityType = PurchaseEvaluationEntityType.Workflow,
                        Action = ChangelogAction.Transition,
                        PhaseAtChange = fromPhase,
                        UserId = actorUid,
                        UserName = reviewerName ?? "Hệ thống",
                        Summary = $"{reviewerName} (NV) đã review phase {fromPhase}, chờ TPB confirm",
                        ContextNote = comment,
                    });

                    // Notify TPB cùng dept để confirm. Best effort — fail OK.
                    try
                    {
                        var managers = await db.Users.AsNoTracking()
                            .Where(u => u.DepartmentId == deptId && u.Id != actorUid && u.IsActive)
                            .Select(u => u.Id)
                            .ToListAsync(ct);
                        if (managers.Count > 0)
                        {
                            // Filter: chỉ notify user có role DeptManager (TPB).
                            // Không có direct join với UserRoles ở IApplicationDbContext —
                            // dùng UserManager để filter từng user.
                            foreach (var mgrId in managers)
                            {
                                var mgr = await userManager.FindByIdAsync(mgrId.ToString());
                                if (mgr is null) continue;
                                var roles = await userManager.GetRolesAsync(mgr);
                                if (!roles.Contains(AppRoles.DeptManager)) continue;

                                await notifications.NotifyAsync(
                                    mgrId,
                                    NotificationType.ContractPhaseTransition,
                                    title: $"Phiếu {evaluation.MaPhieu ?? evaluation.TenGoiThau} chờ TPB confirm",
                                    description: $"NV {reviewerName} đã review phase {fromPhase}. Vui lòng vào confirm để workflow tiếp tục.",
                                    href: $"/purchase-evaluations/{evaluation.Id}",
                                    refId: evaluation.Id,
                                    ct: ct);
                            }
                        }
                    }
                    catch { /* notification fail non-critical */ }

                    await db.SaveChangesAsync(ct);
                    return;
                }
            }
        }

        evaluation.SlaWarningSent = false;
        evaluation.Phase = targetPhase;

        var sla = policy.PhaseSla.GetValueOrDefault(targetPhase);
        evaluation.SlaDeadline = sla is null ? null : dateTime.UtcNow.Add(sla.Value);

        db.PurchaseEvaluationApprovals.Add(new PurchaseEvaluationApproval
        {
            PurchaseEvaluationId = evaluation.Id,
            FromPhase = fromPhase,
            ToPhase = targetPhase,
            ApproverUserId = actorUserId,
            Decision = decision,
            Comment = comment,
            ApprovedAt = dateTime.UtcNow,
        });

        // Resolve actor name for changelog
        string? actorName = null;
        if (actorUserId is Guid uid)
        {
            var user = await userManager.FindByIdAsync(uid.ToString());
            actorName = user?.FullName ?? user?.Email;
        }

        db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
        {
            PurchaseEvaluationId = evaluation.Id,
            EntityType = PurchaseEvaluationEntityType.Workflow,
            Action = ChangelogAction.Transition,
            PhaseAtChange = targetPhase,
            UserId = actorUserId,
            UserName = actorName ?? "Hệ thống",
            Summary = $"Chuyển phase {fromPhase} → {targetPhase}",
            ContextNote = comment,
        });

        // Notify drafter
        if (evaluation.DrafterUserId is Guid drafterId && drafterId != actorUserId)
        {
            var title = targetPhase switch
            {
                PurchaseEvaluationPhase.DaDuyet => $"Phiếu {evaluation.MaPhieu ?? evaluation.TenGoiThau} đã duyệt",
                PurchaseEvaluationPhase.TuChoi => $"Phiếu {evaluation.TenGoiThau} bị từ chối",
                _ => $"Phiếu {evaluation.TenGoiThau} chuyển phase mới",
            };
            var type = targetPhase switch
            {
                PurchaseEvaluationPhase.DaDuyet => NotificationType.ContractPublished,
                PurchaseEvaluationPhase.TuChoi => NotificationType.ContractRejected,
                _ => NotificationType.ContractPhaseTransition,
            };
            await notifications.NotifyAsync(
                drafterId, type, title,
                description: $"{fromPhase} → {targetPhase}" + (string.IsNullOrWhiteSpace(comment) ? "" : $" · {comment}"),
                href: $"/purchase-evaluations/{evaluation.Id}",
                refId: evaluation.Id,
                ct: ct);
        }

        await db.SaveChangesAsync(ct);
    }
}
