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 = PE.SelectedSupplierId, ProjectId, DepartmentId, TenHopDong,
// GiaTri (sum ThanhTienNganSach của details). 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á.
public record CreateContractFromEvaluationCommand(
    Guid PurchaseEvaluationId,
    ContractType ContractType,
    string? TenHopDong,
    bool BypassProcurementAndCCM = false) : IRequest<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, Guid>
{
    public async Task<Guid> Handle(CreateContractFromEvaluationCommand request, CancellationToken ct)
    {
        var pe = await db.PurchaseEvaluations
            .Include(p => p.Details)
            .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).");
        if (pe.SelectedSupplierId is null)
            throw new ConflictException("Phiếu chưa chọn NCC thắng — 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 supplier = await db.Suppliers.FirstOrDefaultAsync(s => s.Id == pe.SelectedSupplierId, ct)
            ?? throw new NotFoundException("Supplier", pe.SelectedSupplierId.Value);
        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);

        var giaTri = pe.Details.Sum(d => d.ThanhTienNganSach);

        var contract = new Contract
        {
            Type = request.ContractType,
            Phase = ContractPhase.DangSoanThao,
            SupplierId = pe.SelectedSupplierId.Value,
            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
            BudgetId = pe.BudgetId,        // carry forward Budget link nếu PE đã link
            BudgetManualName = pe.BudgetManualName,    // carry forward manual budget (Mig 17)
            BudgetManualAmount = pe.BudgetManualAmount,
            WorkflowDefinitionId = activeWfId,
            SlaDeadline = DateTime.UtcNow.Add(
                workflow.GetPhaseSla(ContractPhase.DangSoanThao) ?? TimeSpan.FromDays(7)),
        };
        contract.MaHopDong = await codeGenerator.GenerateAsync(contract, project.Code, supplier.Code, ct);

        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}",
        });

        // Link 2 chiều
        pe.ContractId = contract.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}",
        });

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

// 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)
    {
        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()
            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,
                e.SelectedSupplierId, s != null ? s.Name : null,
                e.ContractId, e.SlaDeadline, e.CreatedAt)).ToListAsync(ct);
    }
}
