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

namespace SolutionErp.Application.PurchaseEvaluations;

// [Mig 54 2026-06-18 — anh Kiệt FDC] 2 handler nhập GIÁ ĐỀ XUẤT tại mục "c. Giá chào
// thầu" theo ROLE — NGOÀI giá NCC báo lên (WinnerQuoteTotal computed):
//   - PRO (Procurement | Admin): ProSuggestedMinPrice + ProSuggestedMaxPrice (dải giá;
//     chỉ 1 trong 2 = hiểu là giá chốt đó).
//   - CCM (CostControl | Admin): CcmSuggestedPrice (1 giá để CEO nhìn + duyệt theo).
// Authz mirror UpdatePeBudgetPro/Ccm (S61): controller [Authorize] any-auth, handler
// ForbiddenException fail-closed TRƯỚC mọi side-effect (S56 #5). KHÔNG ràng Phase
// (mirror ngân sách — chỉnh được như tài liệu sống; trade-off ghi nhận). Set per-PHIẾU
// trực tiếp trên PurchaseEvaluation (KHÔNG per-cặp như ngân sách — giá chào thầu là của
// phiếu, không dùng chung mọi phiếu cùng Hạng mục).

// ===== PRO — dải giá đề xuất Min/Max =====

public record UpdatePeSuggestedPriceProCommand(
    Guid PeId,
    decimal? MinPrice,
    decimal? MaxPrice,
    string? Note = null) : IRequest;     // [Mig 57] ghi chú giải thích min vs max

public class UpdatePeSuggestedPriceProCommandValidator : AbstractValidator<UpdatePeSuggestedPriceProCommand>
{
    public UpdatePeSuggestedPriceProCommandValidator()
    {
        RuleFor(x => x.MinPrice).GreaterThanOrEqualTo(0).When(x => x.MinPrice.HasValue);
        RuleFor(x => x.MaxPrice).GreaterThanOrEqualTo(0).When(x => x.MaxPrice.HasValue);
        // Cả 2 có → Min ≤ Max. Chỉ 1 trong 2 = giá chốt đó (không ràng).
        RuleFor(x => x).Must(x => x.MinPrice!.Value <= x.MaxPrice!.Value)
            .When(x => x.MinPrice.HasValue && x.MaxPrice.HasValue)
            .WithMessage("Giá Min phải ≤ Giá Max.");
        RuleFor(x => x.Note).MaximumLength(1000);     // [Mig 57] MATCH EF HasMaxLength(1000)
    }
}

public class UpdatePeSuggestedPriceProCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<UpdatePeSuggestedPriceProCommand>
{
    public async Task Handle(UpdatePeSuggestedPriceProCommand request, CancellationToken ct)
    {
        var pe = await db.PurchaseEvaluations.FirstOrDefaultAsync(x => x.Id == request.PeId, ct)
            ?? throw new NotFoundException("PurchaseEvaluation", request.PeId);

        // Fail-closed TRƯỚC mọi side-effect.
        var isAdmin = currentUser.Roles.Contains(AppRoles.Admin);
        if (!isAdmin && !currentUser.Roles.Contains(AppRoles.Procurement))
        {
            throw new ForbiddenException(
                "Chỉ Phòng Cung ứng (PRO) hoặc Admin được nhập giá đề xuất PRO (Min/Max).");
        }

        // [S96 2026-07-01 — anh Kiệt FDC "duyệt là fix"] Chỉ sửa giá đề xuất khi phiếu CÒN
        // soạn/trả-lại. Phiếu ĐÃ GỬI DUYỆT (ChoDuyet/sub-phase) / ĐÃ DUYỆT (DaDuyet) / TỪ CHỐI
        // → KHÓA: giá đã trình cho cấp duyệt xem, KHÔNG đổi ngầm dưới chân họ. Đảo comment cũ
        // "KHÔNG ràng Phase" (Mig 54). Admin override (sửa-sai). Non-admin fail-closed.
        if (!isAdmin
            && pe.Phase != PurchaseEvaluationPhase.DangSoanThao
            && pe.Phase != PurchaseEvaluationPhase.TraLai)
        {
            throw new ConflictException(
                "Phiếu đã gửi duyệt / đã duyệt — không sửa được giá đề xuất PRO. Chỉ sửa khi phiếu ở Nháp hoặc Trả lại.");
        }

        var oldMin = pe.ProSuggestedMinPrice;
        var oldMax = pe.ProSuggestedMaxPrice;
        var oldNote = pe.ProSuggestedPriceNote;
        pe.ProSuggestedMinPrice = request.MinPrice;   // absolute-set (null = clear)
        pe.ProSuggestedMaxPrice = request.MaxPrice;
        pe.ProSuggestedPriceNote = request.Note;      // [Mig 57] absolute-set (null = clear)

        var parts = new List<string>();
        if (oldMin != request.MinPrice)
            parts.Add($"giá Min {oldMin?.ToString("N0") ?? "(trống)"}đ → {request.MinPrice?.ToString("N0") ?? "(trống)"}đ");
        if (oldMax != request.MaxPrice)
            parts.Add($"giá Max {oldMax?.ToString("N0") ?? "(trống)"}đ → {request.MaxPrice?.ToString("N0") ?? "(trống)"}đ");
        if (oldNote != request.Note)
            parts.Add("ghi chú giá");

        db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
        {
            PurchaseEvaluationId = pe.Id,
            EntityType = PurchaseEvaluationEntityType.Header,
            Action = ChangelogAction.Update,
            PhaseAtChange = pe.Phase,
            UserId = currentUser.UserId,
            UserName = currentUser.FullName ?? currentUser.Email,
            Summary = $"Giá đề xuất (PRO): {(parts.Count == 0 ? "không đổi" : string.Join(", ", parts))}",
        });

        await db.SaveChangesAsync(ct);
    }
}

// ===== CCM — 1 giá đề xuất (để CEO nhìn + duyệt theo) =====

public record UpdatePeSuggestedPriceCcmCommand(
    Guid PeId,
    decimal? CcmPrice,
    string? Note = null) : IRequest;     // [Mig 57] ghi chú giải thích giá CCM

public class UpdatePeSuggestedPriceCcmCommandValidator : AbstractValidator<UpdatePeSuggestedPriceCcmCommand>
{
    public UpdatePeSuggestedPriceCcmCommandValidator()
    {
        RuleFor(x => x.CcmPrice).GreaterThanOrEqualTo(0).When(x => x.CcmPrice.HasValue);
        RuleFor(x => x.Note).MaximumLength(1000);     // [Mig 57] MATCH EF HasMaxLength(1000)
    }
}

public class UpdatePeSuggestedPriceCcmCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<UpdatePeSuggestedPriceCcmCommand>
{
    public async Task Handle(UpdatePeSuggestedPriceCcmCommand request, CancellationToken ct)
    {
        var pe = await db.PurchaseEvaluations.FirstOrDefaultAsync(x => x.Id == request.PeId, ct)
            ?? throw new NotFoundException("PurchaseEvaluation", request.PeId);

        var isAdmin = currentUser.Roles.Contains(AppRoles.Admin);
        if (!isAdmin && !currentUser.Roles.Contains(AppRoles.CostControl))
        {
            throw new ForbiddenException(
                "Chỉ Phòng Kiểm soát Chi phí (CCM) hoặc Admin được nhập giá đề xuất CCM.");
        }

        // [S96 2026-07-01 — anh Kiệt FDC "duyệt là fix"] Chỉ sửa giá CCM khi phiếu CÒN soạn/
        // trả-lại — đã gửi duyệt / đã duyệt / từ chối → KHÓA. Admin override. (mirror PRO handler)
        if (!isAdmin
            && pe.Phase != PurchaseEvaluationPhase.DangSoanThao
            && pe.Phase != PurchaseEvaluationPhase.TraLai)
        {
            throw new ConflictException(
                "Phiếu đã gửi duyệt / đã duyệt — không sửa được giá đề xuất CCM. Chỉ sửa khi phiếu ở Nháp hoặc Trả lại.");
        }

        var oldCcm = pe.CcmSuggestedPrice;
        var oldNote = pe.CcmSuggestedPriceNote;
        pe.CcmSuggestedPrice = request.CcmPrice;          // absolute-set (null = clear)
        pe.CcmSuggestedPriceNote = request.Note;          // [Mig 57] absolute-set (null = clear)

        var noteChanged = oldNote != request.Note ? " (kèm ghi chú)" : "";
        db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
        {
            PurchaseEvaluationId = pe.Id,
            EntityType = PurchaseEvaluationEntityType.Header,
            Action = ChangelogAction.Update,
            PhaseAtChange = pe.Phase,
            UserId = currentUser.UserId,
            UserName = currentUser.FullName ?? currentUser.Email,
            Summary = $"Giá đề xuất (CCM): {oldCcm?.ToString("N0") ?? "(trống)"}đ → {request.CcmPrice?.ToString("N0") ?? "(trống)"}đ{noteChanged}",
        });

        await db.SaveChangesAsync(ct);
    }
}
