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

namespace SolutionErp.Application.PurchaseEvaluations;

// Kế thừa từ phiếu Duyệt NCC đã DaDuyet → tạo HĐ Draft mới. Map cơ bản:
// SupplierId = mỗi NCC winner (DERIVED IsWinner == Any IsSelected), ProjectId, DepartmentId,
// TenHopDong, GiaTri = SUM Quote.ThanhTien các báo giá IsSelected của NCC đó (S114 D4). ContractType do user chọn
// (phiếu có thể gen HĐ ThauPhu/NhaCungCap/MuaBan... tùy gói thầu).
//
// KHÔNG copy Details per-type automatically — user điền riêng sau khi HĐ
// gen, tránh mapping sai (PE detail schema ≠ 7 Contract detail schemas).
// User có thể reference PE qua PE.ContractId để xem lại báo giá.
// [Mig 58 multi-winner] Trả List<Guid> — 1 HĐ cho MỖI đơn vị IsWinner (liên-danh ≥2 →
// N hợp đồng draft; single winner → 1 phần tử, hành vi cũ giữ nguyên).
public record CreateContractFromEvaluationCommand(
    Guid PurchaseEvaluationId,
    ContractType ContractType,
    string? TenHopDong,
    bool BypassProcurementAndCCM = false) : IRequest<List<Guid>>;

public class CreateContractFromEvaluationCommandValidator : AbstractValidator<CreateContractFromEvaluationCommand>
{
    public CreateContractFromEvaluationCommandValidator()
    {
        RuleFor(x => x.PurchaseEvaluationId).NotEmpty();
        RuleFor(x => x.ContractType).IsInEnum();
        RuleFor(x => x.TenHopDong).MaximumLength(500);
    }
}

public class CreateContractFromEvaluationCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser,
    IContractWorkflowService workflow,
    IContractCodeGenerator codeGenerator) : IRequestHandler<CreateContractFromEvaluationCommand, List<Guid>>
{
    public async Task<List<Guid>> Handle(CreateContractFromEvaluationCommand request, CancellationToken ct)
    {
        var pe = await db.PurchaseEvaluations
            .Include(p => p.Details).ThenInclude(d => d.Quotes)   // [Mig 58] Quotes → GiaTri per-winner
            .Include(p => p.Suppliers)                            // [Mig 58] IsWinner = tập winner
            .FirstOrDefaultAsync(p => p.Id == request.PurchaseEvaluationId, ct)
            ?? throw new NotFoundException("PurchaseEvaluation", request.PurchaseEvaluationId);

        if (pe.Phase != PurchaseEvaluationPhase.DaDuyet)
            throw new ConflictException("Chỉ tạo HĐ từ phiếu đã duyệt xong (DaDuyet).");
        // [Mig 58 multi-winner] ≥1 đơn vị IsWinner (thay SelectedSupplierId single — null khi ≥2).
        var winners = pe.Suppliers.Where(s => s.IsWinner).ToList();
        if (winners.Count == 0)
            throw new ConflictException("Phiếu chưa chọn NCC/TP trúng thầu — click 'Chọn NCC' trước.");
        if (pe.ContractId is not null)
            throw new ConflictException("Phiếu này đã tạo HĐ rồi.");

        var supplierIds = winners.Select(w => w.SupplierId).ToList();
        var supplierMap = await db.Suppliers.Where(s => supplierIds.Contains(s.Id))
            .ToDictionaryAsync(s => s.Id, ct);
        var project = await db.Projects.FirstOrDefaultAsync(p => p.Id == pe.ProjectId, ct)
            ?? throw new NotFoundException("Project", pe.ProjectId);

        var activeWfId = await db.WorkflowDefinitions.AsNoTracking()
            .Where(w => w.ContractType == request.ContractType && w.IsActive)
            .Select(w => (Guid?)w.Id)
            .FirstOrDefaultAsync(ct);

        // [multi-NCC 2026-07-12 — anh Kiệt LOCKED A1/D4] GiaTri mỗi HĐ = SUM báo giá ĐƯỢC CHỌN
        // (Quote.IsSelected) của NCC đó — CẢ single lẫn multi. BỎ shortcut detailsSum cũ (single
        // trước đây = tổng ngân sách; NAY = giá thầu chào, đúng D4 "1 HĐ/NCC = SUM IsSelected
        // quotes của NCC"). 0 nếu NCC chưa có báo giá được chọn.

        // [Mig 58] Codegen TỰ-COMMIT (transaction + ContractCodeSequence RIÊNG mỗi call) → gen
        // TẤT CẢ mã TRƯỚC khi Add vào context, build N Contract trong list LOCAL → SaveChanges
        // nội-bộ codegen KHÔNG flush contract dở. Sau đó Add cả N + final SaveChanges 1 LẦN =
        // atomic (không tạo HĐ dở-dang nếu 1 bước lỗi; chỉ phí vài số seq — chấp nhận được).
        var contracts = new List<Contract>();
        foreach (var w in winners)
        {
            if (!supplierMap.TryGetValue(w.SupplierId, out var supplier))
                throw new NotFoundException("Supplier", w.SupplierId);

            var giaTri = pe.Details.SelectMany(d => d.Quotes)
                .Where(q => q.PurchaseEvaluationSupplierId == w.Id && q.IsSelected)
                .Sum(q => q.ThanhTien);

            var contract = new Contract
            {
                Type = request.ContractType,
                Phase = ContractPhase.DangSoanThao,
                SupplierId = w.SupplierId,
                ProjectId = pe.ProjectId,
                DepartmentId = pe.DepartmentId,
                DrafterUserId = currentUser.UserId,
                GiaTri = giaTri,
                TenHopDong = request.TenHopDong ?? pe.TenGoiThau,
                NoiDung = pe.MoTa,
                BypassProcurementAndCCM = request.BypassProcurementAndCCM,
                DraftData = pe.PaymentTerms,   // carry forward payment terms
                // [S61 Mig 50] kế thừa "Ngân sách kỳ này" phiếu → ngân sách nhập tay HĐ (sửa sau).
                BudgetManualName = pe.MaPhieu is null ? null : $"NS kỳ này phiếu {pe.MaPhieu}",
                BudgetManualAmount = pe.BudgetPeriodAmount,
                WorkflowDefinitionId = activeWfId,
                SlaDeadline = DateTime.UtcNow.Add(
                    workflow.GetPhaseSla(ContractPhase.DangSoanThao) ?? TimeSpan.FromDays(7)),
            };
            // codegen self-commit — contract CHƯA Add nên SaveChanges nội-bộ chỉ flush seq.
            contract.MaHopDong = await codeGenerator.GenerateAsync(contract, project.Code, supplier.Code, ct);
            contracts.Add(contract);
        }

        foreach (var contract in contracts)
        {
            db.Contracts.Add(contract);
            // Changelog HĐ: note kế thừa từ phiếu
            db.ContractChangelogs.Add(new ContractChangelog
            {
                ContractId = contract.Id,
                EntityType = ChangelogEntityType.Contract,
                Action = ChangelogAction.Insert,
                PhaseAtChange = contract.Phase,
                UserId = currentUser.UserId,
                Summary = $"Tạo HĐ {contract.MaHopDong} từ phiếu {pe.MaPhieu ?? pe.TenGoiThau}",
                ContextNote = $"Kế thừa từ PurchaseEvaluation {pe.Id}",
            });
            db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
            {
                PurchaseEvaluationId = pe.Id,
                EntityType = PurchaseEvaluationEntityType.Header,
                Action = ChangelogAction.Update,
                PhaseAtChange = pe.Phase,
                UserId = currentUser.UserId,
                Summary = $"Tạo HĐ {contract.MaHopDong} từ phiếu",
                ContextNote = $"Contract {contract.Id}",
            });
        }

        // Idempotency: pe.ContractId = HĐ ĐẦU (chặn tạo lại). N HĐ liên-danh truy qua changelog +
        // Contract (FE nhận list contractIds). 1 phiếu = 1 link chính + N hợp đồng.
        pe.ContractId = contracts[0].Id;

        await db.SaveChangesAsync(ct);
        return contracts.Select(c => c.Id).ToList();
    }
}

// List phiếu đã duyệt chưa gen HĐ (cho FE modal picker trong ContractCreatePage)
public record ListApprovedPurchaseEvaluationsQuery : IRequest<List<PurchaseEvaluationListItemDto>>;

public class ListApprovedPurchaseEvaluationsQueryHandler(IApplicationDbContext db)
    : IRequestHandler<ListApprovedPurchaseEvaluationsQuery, List<PurchaseEvaluationListItemDto>>
{
    public async Task<List<PurchaseEvaluationListItemDto>> Handle(
        ListApprovedPurchaseEvaluationsQuery request, CancellationToken ct)
    {
        // Plan AG4: JOIN Users + Departments LEFT (mirror ListPurchaseEvaluations).
        return await (
            from e in db.PurchaseEvaluations.AsNoTracking()
            join p in db.Projects.AsNoTracking() on e.ProjectId equals p.Id
            join s in db.Suppliers.AsNoTracking() on e.SelectedSupplierId equals s.Id into sj
            from s in sj.DefaultIfEmpty()
            join u in db.Users.AsNoTracking() on e.DrafterUserId equals u.Id into uj
            from u in uj.DefaultIfEmpty()
            join d in db.Departments.AsNoTracking() on e.DepartmentId equals d.Id into dj
            from d in dj.DefaultIfEmpty()
            join wi in db.WorkItems.AsNoTracking() on e.WorkItemId equals wi.Id into wij
            from wi in wij.DefaultIfEmpty()
            where e.Phase == PurchaseEvaluationPhase.DaDuyet && e.ContractId == null
            orderby e.CreatedAt descending
            select new PurchaseEvaluationListItemDto(
                e.Id, e.MaPhieu, e.TenGoiThau, e.Type, e.Phase,
                e.ProjectId, p.Name, p.Code,
                e.WorkItemId, wi != null ? wi.Name : null, wi != null ? wi.Code : null,
                e.SelectedSupplierId, s != null ? s.Name : null,
                e.ContractId, e.SlaDeadline, e.CreatedAt, e.UpdatedAt,
                e.DrafterUserId, u != null ? u.FullName : null,
                e.DepartmentId, d != null ? d.Name : null,
                e.BudgetPeriodAmount, e.ExpectedRemainingAmount,
                e.IsUrgentByPro, e.IsUrgentByCcm,
                (from sw in db.PurchaseEvaluationSuppliers
                 where sw.PurchaseEvaluationId == e.Id && sw.IsWinner
                 join sup in db.Suppliers on sw.SupplierId equals sup.Id
                 orderby sup.Name
                 select sup.Name).ToList(),                       // [S69] cờ gấp + [Mig 58] winner names
                // [S89 Mig 58] "Duyệt là KẾT THÚC (không trình CEO)" — FinalizeStep/LevelName mirror
                // winner-names (EF-safe). [S97] EndsBeforeCeo: ListApproved = DaDuyet-only (filter trên)
                // → đọc THẲNG runtime flag EndedByLevelFinalize (phiếu opt-out lên CEO → false, hết badge sai).
                e.EndedByLevelFinalize,
                (from lv in db.ApprovalWorkflowLevels
                 where lv.AllowApproverFinalize && lv.Step!.ApprovalWorkflowId == e.ApprovalWorkflowId
                 orderby lv.Step!.Order, lv.Order
                 select lv.Step!.Name).FirstOrDefault(),
                (from lv in db.ApprovalWorkflowLevels
                 where lv.AllowApproverFinalize && lv.Step!.ApprovalWorkflowId == e.ApprovalWorkflowId
                 orderby lv.Step!.Order, lv.Order
                 select lv.Name).FirstOrDefault())).ToListAsync(ct);
    }
}
