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

namespace SolutionErp.Application.PurchaseEvaluations;

// ========== Helper: Lock edit guard (Phase 9 — Migration 16) ==========
// Chỉ Phase=DangSoanThao mới được CRUD chi tiết / báo giá / NCC tham gia.
// Đã trình duyệt → KHÔNG sửa được. Phải reject về DangSoanThao trước.
internal static class PurchaseEvaluationDraftGuard
{
    public static async Task<PurchaseEvaluation> EnsureDraftAsync(IApplicationDbContext db, Guid id, CancellationToken ct)
    {
        var pe = await db.PurchaseEvaluations.FirstOrDefaultAsync(x => x.Id == id, ct)
            ?? throw new NotFoundException("PurchaseEvaluation", id);
        if (pe.Phase != PurchaseEvaluationPhase.DangSoanThao)
            throw new ConflictException($"Phiếu PE đã trình duyệt (Phase={pe.Phase}), không thể chỉnh sửa chi tiết. Phải reject để Drafter sửa lại.");
        return pe;
    }
}

// ========== Detail (hạng mục + ngân sách) ==========

public record AddPurchaseEvaluationDetailCommand(
    Guid PurchaseEvaluationId,
    string GroupCode,
    string GroupName,
    string? ItemCode,
    string NoiDung,
    string? DonViTinh,
    decimal KhoiLuongNganSach,
    decimal KhoiLuongThiCong,
    decimal DonGiaNganSach,
    decimal ThanhTienNganSach,
    string? GhiChu) : IRequest<Guid>;

public class AddPurchaseEvaluationDetailCommandValidator : AbstractValidator<AddPurchaseEvaluationDetailCommand>
{
    public AddPurchaseEvaluationDetailCommandValidator()
    {
        RuleFor(x => x.PurchaseEvaluationId).NotEmpty();
        RuleFor(x => x.GroupCode).NotEmpty().MaximumLength(50);
        RuleFor(x => x.GroupName).NotEmpty().MaximumLength(200);
        RuleFor(x => x.NoiDung).NotEmpty().MaximumLength(500);
    }
}

public class AddPurchaseEvaluationDetailCommandHandler(
    IApplicationDbContext db,
    ICurrentUser currentUser) : IRequestHandler<AddPurchaseEvaluationDetailCommand, Guid>
{
    public async Task<Guid> Handle(AddPurchaseEvaluationDetailCommand request, CancellationToken ct)
    {
        var evaluation = await PurchaseEvaluationDraftGuard.EnsureDraftAsync(db, request.PurchaseEvaluationId, ct);

        var maxOrder = await db.PurchaseEvaluationDetails
            .Where(d => d.PurchaseEvaluationId == request.PurchaseEvaluationId)
            .Select(d => (int?)d.Order)
            .MaxAsync(ct);

        var entity = new PurchaseEvaluationDetail
        {
            PurchaseEvaluationId = request.PurchaseEvaluationId,
            GroupCode = request.GroupCode,
            GroupName = request.GroupName,
            ItemCode = request.ItemCode,
            NoiDung = request.NoiDung,
            DonViTinh = request.DonViTinh,
            KhoiLuongNganSach = request.KhoiLuongNganSach,
            KhoiLuongThiCong = request.KhoiLuongThiCong,
            DonGiaNganSach = request.DonGiaNganSach,
            ThanhTienNganSach = request.ThanhTienNganSach,
            GhiChu = request.GhiChu,
            Order = (maxOrder ?? 0) + 1,
        };
        db.PurchaseEvaluationDetails.Add(entity);

        db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
        {
            PurchaseEvaluationId = request.PurchaseEvaluationId,
            EntityType = PurchaseEvaluationEntityType.Detail,
            EntityId = entity.Id,
            Action = ChangelogAction.Insert,
            PhaseAtChange = evaluation.Phase,
            UserId = currentUser.UserId,
            Summary = $"Thêm hạng mục {request.GroupCode} — {request.NoiDung}",
        });

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

public record UpdatePurchaseEvaluationDetailCommand(
    Guid PurchaseEvaluationId,
    Guid DetailId,
    string GroupCode,
    string GroupName,
    string? ItemCode,
    string NoiDung,
    string? DonViTinh,
    decimal KhoiLuongNganSach,
    decimal KhoiLuongThiCong,
    decimal DonGiaNganSach,
    decimal ThanhTienNganSach,
    string? GhiChu) : IRequest;

public class UpdatePurchaseEvaluationDetailCommandHandler(
    IApplicationDbContext db) : IRequestHandler<UpdatePurchaseEvaluationDetailCommand>
{
    public async Task Handle(UpdatePurchaseEvaluationDetailCommand request, CancellationToken ct)
    {
        await PurchaseEvaluationDraftGuard.EnsureDraftAsync(db, request.PurchaseEvaluationId, ct);
        var entity = await db.PurchaseEvaluationDetails
            .FirstOrDefaultAsync(x => x.Id == request.DetailId && x.PurchaseEvaluationId == request.PurchaseEvaluationId, ct)
            ?? throw new NotFoundException("PurchaseEvaluationDetail", request.DetailId);

        entity.GroupCode = request.GroupCode;
        entity.GroupName = request.GroupName;
        entity.ItemCode = request.ItemCode;
        entity.NoiDung = request.NoiDung;
        entity.DonViTinh = request.DonViTinh;
        entity.KhoiLuongNganSach = request.KhoiLuongNganSach;
        entity.KhoiLuongThiCong = request.KhoiLuongThiCong;
        entity.DonGiaNganSach = request.DonGiaNganSach;
        entity.ThanhTienNganSach = request.ThanhTienNganSach;
        entity.GhiChu = request.GhiChu;

        await db.SaveChangesAsync(ct);
    }
}

public record DeletePurchaseEvaluationDetailCommand(Guid PurchaseEvaluationId, Guid DetailId) : IRequest;

public class DeletePurchaseEvaluationDetailCommandHandler(
    IApplicationDbContext db) : IRequestHandler<DeletePurchaseEvaluationDetailCommand>
{
    public async Task Handle(DeletePurchaseEvaluationDetailCommand request, CancellationToken ct)
    {
        await PurchaseEvaluationDraftGuard.EnsureDraftAsync(db, request.PurchaseEvaluationId, ct);
        var entity = await db.PurchaseEvaluationDetails
            .FirstOrDefaultAsync(x => x.Id == request.DetailId && x.PurchaseEvaluationId == request.PurchaseEvaluationId, ct)
            ?? throw new NotFoundException("PurchaseEvaluationDetail", request.DetailId);

        db.PurchaseEvaluationDetails.Remove(entity);
        await db.SaveChangesAsync(ct);
    }
}

// ========== Quote (báo giá per NCC per Detail) — upsert ==========

public record UpsertPurchaseEvaluationQuoteCommand(
    Guid PurchaseEvaluationId,
    Guid PurchaseEvaluationDetailId,
    Guid PurchaseEvaluationSupplierId,
    decimal BgVat,
    decimal ChuaVat,
    decimal ThanhTien,
    bool IsSelected,
    string? Note) : IRequest<Guid>;

public class UpsertPurchaseEvaluationQuoteCommandHandler(
    IApplicationDbContext db) : IRequestHandler<UpsertPurchaseEvaluationQuoteCommand, Guid>
{
    public async Task<Guid> Handle(UpsertPurchaseEvaluationQuoteCommand request, CancellationToken ct)
    {
        await PurchaseEvaluationDraftGuard.EnsureDraftAsync(db, request.PurchaseEvaluationId, ct);
        // Verify parents exist + same phiếu
        var detail = await db.PurchaseEvaluationDetails.FirstOrDefaultAsync(
            d => d.Id == request.PurchaseEvaluationDetailId && d.PurchaseEvaluationId == request.PurchaseEvaluationId, ct)
            ?? throw new NotFoundException("PurchaseEvaluationDetail", request.PurchaseEvaluationDetailId);

        var supplier = await db.PurchaseEvaluationSuppliers.FirstOrDefaultAsync(
            s => s.Id == request.PurchaseEvaluationSupplierId && s.PurchaseEvaluationId == request.PurchaseEvaluationId, ct)
            ?? throw new NotFoundException("PurchaseEvaluationSupplier", request.PurchaseEvaluationSupplierId);

        var existing = await db.PurchaseEvaluationQuotes.FirstOrDefaultAsync(
            q => q.PurchaseEvaluationDetailId == request.PurchaseEvaluationDetailId
                && q.PurchaseEvaluationSupplierId == request.PurchaseEvaluationSupplierId, ct);

        if (existing is not null)
        {
            existing.BgVat = request.BgVat;
            existing.ChuaVat = request.ChuaVat;
            existing.ThanhTien = request.ThanhTien;
            existing.IsSelected = request.IsSelected;
            existing.Note = request.Note;
            await db.SaveChangesAsync(ct);
            return existing.Id;
        }

        var entity = new PurchaseEvaluationQuote
        {
            PurchaseEvaluationDetailId = request.PurchaseEvaluationDetailId,
            PurchaseEvaluationSupplierId = request.PurchaseEvaluationSupplierId,
            BgVat = request.BgVat,
            ChuaVat = request.ChuaVat,
            ThanhTien = request.ThanhTien,
            IsSelected = request.IsSelected,
            Note = request.Note,
        };
        db.PurchaseEvaluationQuotes.Add(entity);
        await db.SaveChangesAsync(ct);
        return entity.Id;
    }
}

public record DeletePurchaseEvaluationQuoteCommand(Guid PurchaseEvaluationId, Guid QuoteId) : IRequest;

public class DeletePurchaseEvaluationQuoteCommandHandler(
    IApplicationDbContext db) : IRequestHandler<DeletePurchaseEvaluationQuoteCommand>
{
    public async Task Handle(DeletePurchaseEvaluationQuoteCommand request, CancellationToken ct)
    {
        await PurchaseEvaluationDraftGuard.EnsureDraftAsync(db, request.PurchaseEvaluationId, ct);
        var quote = await (
            from q in db.PurchaseEvaluationQuotes
            join d in db.PurchaseEvaluationDetails on q.PurchaseEvaluationDetailId equals d.Id
            where q.Id == request.QuoteId && d.PurchaseEvaluationId == request.PurchaseEvaluationId
            select q).FirstOrDefaultAsync(ct)
            ?? throw new NotFoundException("PurchaseEvaluationQuote", request.QuoteId);

        db.PurchaseEvaluationQuotes.Remove(quote);
        await db.SaveChangesAsync(ct);
    }
}

// ========== Select winner (NCC được chọn tổng thể) ==========

public record SelectPurchaseEvaluationWinnerCommand(Guid PurchaseEvaluationId, Guid SupplierId) : IRequest;

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

        _ = await db.Suppliers.FirstOrDefaultAsync(s => s.Id == request.SupplierId, ct)
            ?? throw new NotFoundException("Supplier", request.SupplierId);

        // Verify supplier nằm trong danh sách phiếu
        var hasSupplier = await db.PurchaseEvaluationSuppliers
            .AnyAsync(s => s.PurchaseEvaluationId == request.PurchaseEvaluationId && s.SupplierId == request.SupplierId, ct);
        if (!hasSupplier) throw new ConflictException("NCC chưa được thêm vào phiếu đánh giá.");

        entity.SelectedSupplierId = request.SupplierId;

        db.PurchaseEvaluationChangelogs.Add(new PurchaseEvaluationChangelog
        {
            PurchaseEvaluationId = entity.Id,
            EntityType = PurchaseEvaluationEntityType.Header,
            Action = ChangelogAction.Update,
            PhaseAtChange = entity.Phase,
            UserId = currentUser.UserId,
            Summary = "Chọn NCC trúng thầu",
        });

        await db.SaveChangesAsync(ct);
    }
}
