using System.Data;
using FluentValidation;
using MediatR;
using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Application.Common.Models;
using SolutionErp.Domain.ApprovalWorkflowsV2;
using SolutionErp.Domain.Office;

namespace SolutionErp.Application.Office;

// Phase 10.3 G-O3 (S37 2026-05-28) — Đề xuất (Proposal) CQRS + Workflow V2 ApproveV2.
// Cookie-cutter mirror PurchaseEvaluation Plan B Chunk D pattern (Mig 22-26 V2).
// ApplicableType=ProposalGeneral=4 (Mig 37 enum extend).
//
// 8 endpoint qua ProposalsController:
//   GET    /api/proposals                — list paged (filter status/dept/drafter/inbox)
//   GET    /api/proposals/{id}           — detail Include Attachments + LevelOpinions + Workflow
//   POST   /api/proposals                — create Status=Nhap (MaDeXuat null tới Submit)
//   PUT    /api/proposals/{id}           — update draft (Nhap or TraLai only)
//   POST   /api/proposals/{id}/submit    — gen MaDeXuat atomic + Status=DaGuiDuyet
//   POST   /api/proposals/{id}/approve   — ApproveV2: UPSERT LevelOpinion + advance level/terminal
//   POST   /api/proposals/{id}/reject    — Status=TuChoi terminal (no opinion sync)
//   POST   /api/proposals/{id}/return    — Status=TraLai (no opinion sync, Drafter resubmit từ Cấp 1)

// ===== DTOs =====

public record ProposalListItemDto(
    Guid Id,
    string? MaDeXuat,
    string Title,
    decimal? AmountEstimate,
    int Status,
    Guid? DepartmentId,
    string? DepartmentName,
    Guid DrafterUserId,
    string? DrafterFullName,
    Guid? ApprovalWorkflowId,
    string? WorkflowCode,
    int? CurrentApprovalLevelOrder,
    DateTime CreatedAt);

public record ProposalDetailDto(
    Guid Id,
    string? MaDeXuat,
    string Title,
    string? Description,
    decimal? AmountEstimate,
    int Status,
    Guid? DepartmentId,
    string? DepartmentName,
    Guid DrafterUserId,
    string? DrafterFullName,
    Guid? ApprovalWorkflowId,
    string? WorkflowCode,
    string? WorkflowName,
    int? CurrentApprovalLevelOrder,
    int? RejectedFromStatus,
    DateTime CreatedAt,
    List<ProposalAttachmentDto> Attachments,
    List<ProposalLevelOpinionDto> LevelOpinions);

public record ProposalAttachmentDto(
    Guid Id,
    string FileName,
    string FilePath,
    long FileSize,
    string? MimeType,
    Guid UploadedByUserId,
    string UploadedByFullName,
    DateTime CreatedAt);

public record ProposalLevelOpinionDto(
    Guid Id,
    Guid ApprovalWorkflowLevelId,
    int? StepOrder,
    string? StepName,
    int? LevelOrder,
    Guid? ApproverUserId,
    string? Comment,
    DateTime SignedAt,
    Guid SignedByUserId,
    string SignedByFullName);

// ===== Region 1: Header CRUD =====

public record GetProposalsQuery(
    int? Status,
    Guid? DepartmentId,
    Guid? DrafterUserId,
    bool InboxOnly = false,
    int Page = 1,
    int PageSize = 50,
    string? Search = null) : IRequest<PagedResult<ProposalListItemDto>>;

public class GetProposalsHandler(IApplicationDbContext db, ICurrentUser currentUser)
    : IRequestHandler<GetProposalsQuery, PagedResult<ProposalListItemDto>>
{
    public async Task<PagedResult<ProposalListItemDto>> Handle(GetProposalsQuery req, CancellationToken ct)
    {
        var page = req.Page < 1 ? 1 : req.Page;
        var pageSize = req.PageSize switch { < 1 => 50, > 200 => 200, _ => req.PageSize };

        var q = db.Proposals.AsNoTracking().Where(p => !p.IsDeleted);

        if (req.Status.HasValue)
            q = q.Where(p => (int)p.Status == req.Status.Value);
        if (req.DepartmentId.HasValue)
            q = q.Where(p => p.DepartmentId == req.DepartmentId.Value);
        if (req.DrafterUserId.HasValue)
            q = q.Where(p => p.DrafterUserId == req.DrafterUserId.Value);

        // Inbox = DaGuiDuyet phiếu có Cấp hiện tại match current user trong Workflow.
        // Lite version: filter Status=DaGuiDuyet client-side resolve approver via Workflow join below.
        if (req.InboxOnly && currentUser.UserId.HasValue)
        {
            q = q.Where(p => p.Status == ProposalStatus.DaGuiDuyet);
        }

        if (!string.IsNullOrWhiteSpace(req.Search))
        {
            var s = req.Search.Trim();
            q = q.Where(p => p.Title.Contains(s) || (p.MaDeXuat != null && p.MaDeXuat.Contains(s)));
        }

        var total = await q.CountAsync(ct);

        var items = await q.OrderByDescending(p => p.CreatedAt)
            .Skip((page - 1) * pageSize)
            .Take(pageSize)
            .Select(p => new ProposalListItemDto(
                p.Id,
                p.MaDeXuat,
                p.Title,
                p.AmountEstimate,
                (int)p.Status,
                p.DepartmentId,
                db.Departments.Where(d => d.Id == p.DepartmentId).Select(d => d.Name).FirstOrDefault(),
                p.DrafterUserId,
                db.Users.Where(u => u.Id == p.DrafterUserId).Select(u => u.FullName).FirstOrDefault(),
                p.ApprovalWorkflowId,
                db.ApprovalWorkflows.Where(w => w.Id == p.ApprovalWorkflowId).Select(w => w.Code).FirstOrDefault(),
                p.CurrentApprovalLevelOrder,
                p.CreatedAt))
            .ToListAsync(ct);

        return new PagedResult<ProposalListItemDto>(items, total, page, pageSize);
    }
}

public record GetProposalByIdQuery(Guid Id) : IRequest<ProposalDetailDto?>;

public class GetProposalByIdHandler(IApplicationDbContext db)
    : IRequestHandler<GetProposalByIdQuery, ProposalDetailDto?>
{
    public async Task<ProposalDetailDto?> Handle(GetProposalByIdQuery req, CancellationToken ct)
    {
        var p = await db.Proposals.AsNoTracking()
            .Include(x => x.Attachments)
            .Include(x => x.LevelOpinions)
            .FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct);
        if (p is null) return null;

        var deptName = await db.Departments.AsNoTracking()
            .Where(d => d.Id == p.DepartmentId).Select(d => d.Name).FirstOrDefaultAsync(ct);
        var drafterName = await db.Users.AsNoTracking()
            .Where(u => u.Id == p.DrafterUserId).Select(u => u.FullName).FirstOrDefaultAsync(ct);

        string? wfCode = null;
        string? wfName = null;
        if (p.ApprovalWorkflowId.HasValue)
        {
            var wf = await db.ApprovalWorkflows.AsNoTracking()
                .Where(w => w.Id == p.ApprovalWorkflowId.Value)
                .Select(w => new { w.Code, w.Name })
                .FirstOrDefaultAsync(ct);
            wfCode = wf?.Code;
            wfName = wf?.Name;
        }

        // Build LevelOpinions với Step+Level metadata via JOIN
        var levelIds = p.LevelOpinions.Select(o => o.ApprovalWorkflowLevelId).Distinct().ToList();
        var levels = await db.ApprovalWorkflowSteps.AsNoTracking()
            .Where(s => s.Levels.Any(l => levelIds.Contains(l.Id)))
            .Select(s => new
            {
                s.Id,
                s.Order,
                s.Name,
                Levels = s.Levels.Where(l => levelIds.Contains(l.Id))
                    .Select(l => new { l.Id, l.Order, l.ApproverUserId })
                    .ToList(),
            })
            .ToListAsync(ct);

        var levelLookup = levels.SelectMany(s => s.Levels.Select(l => new { Step = s, Level = l }))
            .ToDictionary(x => x.Level.Id);

        var opinions = p.LevelOpinions
            .Select(o =>
            {
                levelLookup.TryGetValue(o.ApprovalWorkflowLevelId, out var lvl);
                return new ProposalLevelOpinionDto(
                    o.Id,
                    o.ApprovalWorkflowLevelId,
                    lvl?.Step.Order,
                    lvl?.Step.Name,
                    lvl?.Level.Order,
                    lvl?.Level.ApproverUserId,
                    o.Comment,
                    o.SignedAt,
                    o.SignedByUserId,
                    o.SignedByFullName);
            })
            .OrderBy(o => o.StepOrder).ThenBy(o => o.LevelOrder)
            .ToList();

        var attachments = p.Attachments.Select(a => new ProposalAttachmentDto(
            a.Id, a.FileName, a.FilePath, a.FileSize, a.MimeType,
            a.UploadedByUserId, a.UploadedByFullName, a.CreatedAt)).ToList();

        return new ProposalDetailDto(
            p.Id, p.MaDeXuat, p.Title, p.Description, p.AmountEstimate, (int)p.Status,
            p.DepartmentId, deptName, p.DrafterUserId, drafterName,
            p.ApprovalWorkflowId, wfCode, wfName, p.CurrentApprovalLevelOrder,
            p.RejectedFromStatus.HasValue ? (int)p.RejectedFromStatus.Value : (int?)null,
            p.CreatedAt, attachments, opinions);
    }
}

public record CreateProposalCommand(
    string Title,
    string? Description,
    decimal? AmountEstimate,
    Guid? DepartmentId,
    Guid? ApprovalWorkflowId) : IRequest<Guid>;

public class CreateProposalValidator : AbstractValidator<CreateProposalCommand>
{
    public CreateProposalValidator()
    {
        RuleFor(x => x.Title).NotEmpty().MaximumLength(300);
        RuleFor(x => x.Description).MaximumLength(5000);
        RuleFor(x => x.AmountEstimate).GreaterThanOrEqualTo(0).When(x => x.AmountEstimate.HasValue);
    }
}

public class CreateProposalHandler(IApplicationDbContext db, ICurrentUser currentUser, IDateTime clock)
    : IRequestHandler<CreateProposalCommand, Guid>
{
    public async Task<Guid> Handle(CreateProposalCommand req, CancellationToken ct)
    {
        if (currentUser.UserId is null)
            throw new UnauthorizedException();

        // Verify workflow ApplicableType=ProposalGeneral=4 nếu pin
        if (req.ApprovalWorkflowId.HasValue)
        {
            var wfType = await db.ApprovalWorkflows.AsNoTracking()
                .Where(w => w.Id == req.ApprovalWorkflowId.Value)
                .Select(w => (int?)w.ApplicableType)
                .FirstOrDefaultAsync(ct);
            if (wfType is null)
                throw new NotFoundException("ApprovalWorkflow", req.ApprovalWorkflowId.Value);
            if (wfType.Value != (int)ApprovalWorkflowApplicableType.ProposalGeneral)
                throw new ConflictException("Quy trình duyệt không thuộc loại Đề xuất.");
        }

        var entity = new Proposal
        {
            Title = req.Title.Trim(),
            Description = req.Description?.Trim(),
            AmountEstimate = req.AmountEstimate,
            DepartmentId = req.DepartmentId,
            DrafterUserId = currentUser.UserId.Value,
            ApprovalWorkflowId = req.ApprovalWorkflowId,
            Status = ProposalStatus.Nhap,
            CreatedAt = clock.UtcNow,
            CreatedBy = currentUser.UserId,
        };
        db.Proposals.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record UpdateProposalDraftCommand(
    Guid Id,
    string Title,
    string? Description,
    decimal? AmountEstimate,
    Guid? DepartmentId,
    Guid? ApprovalWorkflowId) : IRequest;

public class UpdateProposalDraftValidator : AbstractValidator<UpdateProposalDraftCommand>
{
    public UpdateProposalDraftValidator()
    {
        RuleFor(x => x.Title).NotEmpty().MaximumLength(300);
        RuleFor(x => x.Description).MaximumLength(5000);
        RuleFor(x => x.AmountEstimate).GreaterThanOrEqualTo(0).When(x => x.AmountEstimate.HasValue);
    }
}

public class UpdateProposalDraftHandler(IApplicationDbContext db, ICurrentUser currentUser, IDateTime clock)
    : IRequestHandler<UpdateProposalDraftCommand>
{
    public async Task Handle(UpdateProposalDraftCommand req, CancellationToken ct)
    {
        var p = await db.Proposals.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct);
        if (p is null) throw new NotFoundException("Proposal", req.Id);

        // Only Drafter (or admin) can edit, only when Nhap/TraLai
        var isOwner = p.DrafterUserId == currentUser.UserId;
        var isAdmin = currentUser.Roles.Contains("Admin");
        if (!isOwner && !isAdmin)
            throw new ForbiddenException("Chỉ người soạn hoặc Admin được sửa đề xuất.");
        if (p.Status != ProposalStatus.Nhap && p.Status != ProposalStatus.TraLai)
            throw new ConflictException("Chỉ sửa được khi trạng thái Nháp hoặc Trả lại.");

        // Verify workflow if changed
        if (req.ApprovalWorkflowId.HasValue && req.ApprovalWorkflowId != p.ApprovalWorkflowId)
        {
            var wfType = await db.ApprovalWorkflows.AsNoTracking()
                .Where(w => w.Id == req.ApprovalWorkflowId.Value)
                .Select(w => (int?)w.ApplicableType)
                .FirstOrDefaultAsync(ct);
            if (wfType is null)
                throw new NotFoundException("ApprovalWorkflow", req.ApprovalWorkflowId.Value);
            if (wfType.Value != (int)ApprovalWorkflowApplicableType.ProposalGeneral)
                throw new ConflictException("Quy trình duyệt không thuộc loại Đề xuất.");
        }

        p.Title = req.Title.Trim();
        p.Description = req.Description?.Trim();
        p.AmountEstimate = req.AmountEstimate;
        p.DepartmentId = req.DepartmentId;
        p.ApprovalWorkflowId = req.ApprovalWorkflowId;
        p.UpdatedAt = clock.UtcNow;
        p.UpdatedBy = currentUser.UserId;
        await db.SaveChangesAsync(ct);
    }
}

// ===== Region 2: Workflow Actions =====

public record SubmitProposalCommand(Guid Id) : IRequest;

public class SubmitProposalHandler(IApplicationDbContext db, ICurrentUser currentUser, IDateTime clock)
    : IRequestHandler<SubmitProposalCommand>
{
    public async Task Handle(SubmitProposalCommand req, CancellationToken ct)
    {
        var p = await db.Proposals.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct);
        if (p is null) throw new NotFoundException("Proposal", req.Id);

        var isOwner = p.DrafterUserId == currentUser.UserId;
        var isAdmin = currentUser.Roles.Contains("Admin");
        if (!isOwner && !isAdmin)
            throw new ForbiddenException("Chỉ người soạn hoặc Admin được gửi duyệt.");
        if (p.Status != ProposalStatus.Nhap && p.Status != ProposalStatus.TraLai)
            throw new ConflictException("Chỉ gửi duyệt được khi trạng thái Nháp hoặc Trả lại.");
        if (!p.ApprovalWorkflowId.HasValue)
            throw new ConflictException("Chưa chọn quy trình duyệt.");

        // Gen MaDeXuat nếu null (lần Submit đầu tiên)
        if (string.IsNullOrEmpty(p.MaDeXuat))
        {
            p.MaDeXuat = await GenerateMaDeXuatAsync(db, clock.Now.Year, clock, ct);
        }

        p.Status = ProposalStatus.DaGuiDuyet;
        p.CurrentApprovalLevelOrder = 1;
        p.RejectedFromStatus = null;
        p.UpdatedAt = clock.UtcNow;
        p.UpdatedBy = currentUser.UserId;
        await db.SaveChangesAsync(ct);
    }

    internal static async Task<string> GenerateMaDeXuatAsync(
        IApplicationDbContext db, int year, IDateTime clock, CancellationToken ct)
    {
        var prefix = $"DX/{year}";
        var dbContext = (DbContext)db;
        await using var tx = await dbContext.Database.BeginTransactionAsync(IsolationLevel.Serializable, ct);
        var seq = await db.ProposalCodeSequences.FirstOrDefaultAsync(s => s.Prefix == prefix, ct);
        if (seq is null)
        {
            seq = new ProposalCodeSequence { Prefix = prefix, LastSeq = 0, UpdatedAt = clock.UtcNow };
            db.ProposalCodeSequences.Add(seq);
        }
        seq.LastSeq++;
        seq.UpdatedAt = clock.UtcNow;
        await db.SaveChangesAsync(ct);
        await tx.CommitAsync(ct);
        return $"{prefix}/{seq.LastSeq:D3}";
    }
}

public record ApproveProposalCommand(Guid Id, string? Comment) : IRequest;

public class ApproveProposalHandler(IApplicationDbContext db, ICurrentUser currentUser, IDateTime clock)
    : IRequestHandler<ApproveProposalCommand>
{
    public async Task Handle(ApproveProposalCommand req, CancellationToken ct)
    {
        if (currentUser.UserId is null) throw new UnauthorizedException();

        var p = await db.Proposals
            .FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct);
        if (p is null) throw new NotFoundException("Proposal", req.Id);
        if (p.Status != ProposalStatus.DaGuiDuyet)
            throw new ConflictException("Chỉ duyệt được khi trạng thái Đã gửi duyệt.");
        if (!p.ApprovalWorkflowId.HasValue || !p.CurrentApprovalLevelOrder.HasValue)
            throw new ConflictException("Quy trình duyệt chưa pin hoặc thiếu cấp hiện tại.");

        var wf = await db.ApprovalWorkflows.AsNoTracking()
            .Include(w => w.Steps).ThenInclude(s => s.Levels)
            .FirstOrDefaultAsync(w => w.Id == p.ApprovalWorkflowId.Value, ct);
        if (wf is null) throw new NotFoundException("ApprovalWorkflow", p.ApprovalWorkflowId.Value);

        // Tìm Step hiện tại = chứa Level Order == CurrentApprovalLevelOrder
        // Multi-step workflow: traverse step-by-step. Lite version: assume 1 step per workflow
        // hoặc CurrentApprovalLevelOrder reset mỗi step (mirror PE design).
        // Đây dùng global level order trong workflow (sum tất cả level cross steps).
        var allLevels = wf.Steps.OrderBy(s => s.Order)
            .SelectMany(s => s.Levels.OrderBy(l => l.Order).Select(l => new { Step = s, Level = l }))
            .ToList();
        if (allLevels.Count == 0)
            throw new ConflictException("Quy trình duyệt không có cấp duyệt.");

        var currentSlot = allLevels.ElementAtOrDefault(p.CurrentApprovalLevelOrder.Value - 1);
        if (currentSlot is null)
            throw new ConflictException($"Cấp duyệt {p.CurrentApprovalLevelOrder.Value} không tồn tại trong quy trình.");

        // Verify actor match — must be ApproverUserId hoặc Admin override
        var isAdmin = currentUser.Roles.Contains("Admin");
        if (!isAdmin && currentSlot.Level.ApproverUserId != currentUser.UserId.Value)
            throw new ForbiddenException("Không phải người duyệt của cấp này.");

        // UPSERT LevelOpinion
        var existing = await db.ProposalLevelOpinions
            .FirstOrDefaultAsync(o => o.ProposalId == p.Id && o.ApprovalWorkflowLevelId == currentSlot.Level.Id, ct);
        var commentFinal = string.IsNullOrWhiteSpace(req.Comment)
            ? "(duyệt — không ý kiến)"
            : req.Comment.Trim();
        if (existing is null)
        {
            db.ProposalLevelOpinions.Add(new ProposalLevelOpinion
            {
                ProposalId = p.Id,
                ApprovalWorkflowLevelId = currentSlot.Level.Id,
                Comment = commentFinal,
                SignedAt = clock.UtcNow,
                SignedByUserId = currentUser.UserId.Value,
                SignedByFullName = currentUser.FullName ?? "(unknown)",
                CreatedAt = clock.UtcNow,
                CreatedBy = currentUser.UserId,
            });
        }
        else
        {
            existing.Comment = commentFinal;
            existing.SignedAt = clock.UtcNow;
            existing.SignedByUserId = currentUser.UserId.Value;
            existing.SignedByFullName = currentUser.FullName ?? "(unknown)";
            existing.UpdatedAt = clock.UtcNow;
            existing.UpdatedBy = currentUser.UserId;
        }

        // Advance level OR terminal
        if (p.CurrentApprovalLevelOrder.Value < allLevels.Count)
        {
            p.CurrentApprovalLevelOrder = p.CurrentApprovalLevelOrder.Value + 1;
        }
        else
        {
            p.Status = ProposalStatus.DaDuyet;
            p.CurrentApprovalLevelOrder = null;
        }
        p.UpdatedAt = clock.UtcNow;
        p.UpdatedBy = currentUser.UserId;
        await db.SaveChangesAsync(ct);
    }
}

public record RejectProposalCommand(Guid Id, string? Comment) : IRequest;

public class RejectProposalHandler(IApplicationDbContext db, ICurrentUser currentUser, IDateTime clock)
    : IRequestHandler<RejectProposalCommand>
{
    public async Task Handle(RejectProposalCommand req, CancellationToken ct)
    {
        var p = await db.Proposals.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct);
        if (p is null) throw new NotFoundException("Proposal", req.Id);
        if (p.Status != ProposalStatus.DaGuiDuyet)
            throw new ConflictException("Chỉ từ chối được khi đang trong workflow duyệt.");

        // Verify actor (lite — only check if has Admin role or matches any level approver)
        var isAdmin = currentUser.Roles.Contains("Admin");
        if (!isAdmin && p.CurrentApprovalLevelOrder.HasValue && p.ApprovalWorkflowId.HasValue)
        {
            var wf = await db.ApprovalWorkflows.AsNoTracking()
                .Include(w => w.Steps).ThenInclude(s => s.Levels)
                .FirstOrDefaultAsync(w => w.Id == p.ApprovalWorkflowId.Value, ct);
            var allLevels = wf?.Steps.OrderBy(s => s.Order)
                .SelectMany(s => s.Levels.OrderBy(l => l.Order))
                .ToList() ?? new();
            var currentLevel = allLevels.ElementAtOrDefault(p.CurrentApprovalLevelOrder.Value - 1);
            if (currentLevel?.ApproverUserId != currentUser.UserId)
                throw new ForbiddenException("Không phải người duyệt của cấp này.");
        }

        p.Status = ProposalStatus.TuChoi;
        p.CurrentApprovalLevelOrder = null;
        p.UpdatedAt = clock.UtcNow;
        p.UpdatedBy = currentUser.UserId;
        await db.SaveChangesAsync(ct);
    }
}

public record ReturnProposalCommand(Guid Id, string? Comment) : IRequest;

public class ReturnProposalHandler(IApplicationDbContext db, ICurrentUser currentUser, IDateTime clock)
    : IRequestHandler<ReturnProposalCommand>
{
    public async Task Handle(ReturnProposalCommand req, CancellationToken ct)
    {
        var p = await db.Proposals.FirstOrDefaultAsync(x => x.Id == req.Id && !x.IsDeleted, ct);
        if (p is null) throw new NotFoundException("Proposal", req.Id);
        if (p.Status != ProposalStatus.DaGuiDuyet)
            throw new ConflictException("Chỉ trả lại được khi đang trong workflow duyệt.");

        var isAdmin = currentUser.Roles.Contains("Admin");
        if (!isAdmin && p.CurrentApprovalLevelOrder.HasValue && p.ApprovalWorkflowId.HasValue)
        {
            var wf = await db.ApprovalWorkflows.AsNoTracking()
                .Include(w => w.Steps).ThenInclude(s => s.Levels)
                .FirstOrDefaultAsync(w => w.Id == p.ApprovalWorkflowId.Value, ct);
            var allLevels = wf?.Steps.OrderBy(s => s.Order)
                .SelectMany(s => s.Levels.OrderBy(l => l.Order))
                .ToList() ?? new();
            var currentLevel = allLevels.ElementAtOrDefault(p.CurrentApprovalLevelOrder.Value - 1);
            if (currentLevel?.ApproverUserId != currentUser.UserId)
                throw new ForbiddenException("Không phải người duyệt của cấp này.");
        }

        p.Status = ProposalStatus.TraLai;
        p.RejectedFromStatus = ProposalStatus.DaGuiDuyet;
        p.CurrentApprovalLevelOrder = null;
        p.UpdatedAt = clock.UtcNow;
        p.UpdatedBy = currentUser.UserId;
        await db.SaveChangesAsync(ct);
    }
}
