using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SolutionErp.Application.Common.Interfaces;
using SolutionErp.Application.PurchaseEvaluations;
using SolutionErp.Domain.Identity;
using SolutionErp.Domain.Master;
using SolutionErp.Domain.PurchaseEvaluations;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// [Mig 58 2026-06-24 — CCM/anh Kiệt FDC — test-before/after] WinnerQuoteTotal multi-winner.
// item 1: winnerQuoteTotal = SUM(báo giá ThanhTien) MỌI supplier-row IsWinner — KHÔNG còn
// theo SelectedSupplierId (nguồn-sự-thật mới = IsWinner). Verify qua GetDetail read DTO
// (GetPurchaseEvaluationQueryHandler → PurchaseEvaluationDetailBundleDto.WinnerQuoteTotal,
// PurchaseEvaluationFeatures.cs ~1090-1097): build winnerSupplierRowIds = Suppliers IsWinner
// → SUM Details.Quotes thuộc các row đó.
//
// Harness mirror PeWorkItemBudgetTests (IdentityFixture + SQLite + GetPurchaseEvaluationQuery
// stack). KHÔNG touch production — test theo CODE đã land (S34). Query handler 3-dep
// (db + UserManager + ICurrentUser); dùng Admin để bỏ qua authz scope.
public class PeMultiWinnerTests
{
    private sealed class FakeCurrentUser : ICurrentUser
    {
        public Guid? UserId { get; init; } = Guid.NewGuid();
        public string? Email { get; init; } = "actor@test.local";
        public string? FullName { get; init; } = "Actor Test";
        public IReadOnlyList<string> Roles { get; init; } = Array.Empty<string>();
        public bool IsAuthenticated => UserId is not null;
    }

    private static FakeCurrentUser AsRoles(params string[] roles) => new() { Roles = roles };

    private static GetPurchaseEvaluationQueryHandler BuildQueryHandler(
        IdentityFixture fix, TestApplicationDbContext db, ICurrentUser currentUser)
        => new(db, fix.Services.GetRequiredService<UserManager<User>>(), currentUser);

    private static async Task<PurchaseEvaluation> SeedPeAsync(TestApplicationDbContext db, string code = "PE-MW")
    {
        var pe = new PurchaseEvaluation
        {
            Id = Guid.NewGuid(),
            Type = PurchaseEvaluationType.DuyetNcc,
            Phase = PurchaseEvaluationPhase.DangSoanThao,
            MaPhieu = code,
            TenGoiThau = "Gói thầu multi-winner",
            ProjectId = Guid.NewGuid(),
            DrafterUserId = Guid.NewGuid(),
        };
        db.PurchaseEvaluations.Add(pe);
        await db.SaveChangesAsync(CancellationToken.None);
        return pe;
    }

    // 1 detail dùng chung cho mọi NCC (mỗi NCC 1 quote thuộc detail này).
    private static async Task<Guid> SeedDetailAsync(TestApplicationDbContext db, PurchaseEvaluation pe)
    {
        var detail = new PurchaseEvaluationDetail
        {
            Id = Guid.NewGuid(),
            PurchaseEvaluationId = pe.Id,
            GroupCode = "A.I",
            GroupName = "Bê tông",
            NoiDung = "Concrete M100",
            Order = 0,
        };
        db.PurchaseEvaluationDetails.Add(detail);
        await db.SaveChangesAsync(CancellationToken.None);
        return detail.Id;
    }

    // 1 NCC tham gia (master + PES row) + 1 quote ThanhTien thuộc detail. isWinner pin
    // IsWinner trên PES row VÀ IsSelected trên quote (invariant IsWinner==Any(IsSelected)).
    // [multi-NCC 2026-07-12 cardinality-change] DTO WinnerQuoteTotal NAY = SUM ThanhTien
    // quote ĐƯỢC CHỌN (Quote.IsSelected — PurchaseEvaluationFeatures.cs:1188-1191
    // `.Where(q => q.IsSelected)`), KHÔNG còn build từ IsWinner supplier-row. Nên winner PHẢI
    // có quote.IsSelected=true thì mới lọt tổng; loser IsSelected=false → bị loại. Return supplierId.
    private static async Task<Guid> SeedSupplierWithQuoteAsync(
        TestApplicationDbContext db, PurchaseEvaluation pe, Guid detailId,
        string code, decimal thanhTien, bool isWinner, int order)
    {
        var supplier = new Supplier { Id = Guid.NewGuid(), Code = code, Name = "NCC " + code };
        db.Suppliers.Add(supplier);
        var pes = new PurchaseEvaluationSupplier
        {
            Id = Guid.NewGuid(),
            PurchaseEvaluationId = pe.Id,
            SupplierId = supplier.Id,
            Order = order,
            IsWinner = isWinner,
        };
        db.PurchaseEvaluationSuppliers.Add(pes);
        db.PurchaseEvaluationQuotes.Add(new PurchaseEvaluationQuote
        {
            Id = Guid.NewGuid(),
            PurchaseEvaluationDetailId = detailId,
            PurchaseEvaluationSupplierId = pes.Id,
            ThanhTien = thanhTien,
            IsSelected = isWinner,   // [multi-NCC] winner ⟺ báo giá được chọn
        });
        await db.SaveChangesAsync(CancellationToken.None);
        return supplier.Id;
    }

    [Fact]
    public async Task WinnerQuoteTotal_ThreeWinners_SumsAllThreeThanhTien()
    {
        // ⭐ TRỤC CỐT LÕI item 1: 3 NCC cùng trúng (IsWinner=true) → WinnerQuoteTotal =
        // SUM cả 3 báo giá (37.8 + 9 + 10 = 56.8tr), KHÔNG chỉ 1 đơn vị.
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var pe = await SeedPeAsync(db, "PE-MW-3WIN");
        var detailId = await SeedDetailAsync(db, pe);

        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-A", 37_800_000m, isWinner: true, order: 0);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-B", 9_000_000m, isWinner: true, order: 1);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-C", 10_000_000m, isWinner: true, order: 2);

        var handler = BuildQueryHandler(fix, db, AsRoles(AppRoles.Admin));
        var bundle = await handler.Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);

        bundle.WinnerQuoteTotal.Should().Be(56_800_000m, "tổng giá gói = SUM báo giá MỌI đơn vị IsWinner");
        bundle.SelectedSupplierId.Should().BeNull("≥2 winner → SelectedSupplierId không sync 1 đơn vị");
    }

    [Fact]
    public async Task WinnerQuoteTotal_ExcludesNonWinnerSuppliers()
    {
        // 4 NCC tham gia, chỉ 2 IsWinner=true → WinnerQuoteTotal CHỈ tính 2 winner
        // (100 + 50 = 150tr), bỏ qua 2 NCC không trúng (200 + 300tr KHÔNG cộng).
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var pe = await SeedPeAsync(db, "PE-MW-EXCL");
        var detailId = await SeedDetailAsync(db, pe);

        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-W1", 100_000_000m, isWinner: true, order: 0);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-W2", 50_000_000m, isWinner: true, order: 1);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-L1", 200_000_000m, isWinner: false, order: 2);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-L2", 300_000_000m, isWinner: false, order: 3);

        var handler = BuildQueryHandler(fix, db, AsRoles(AppRoles.Admin));
        var bundle = await handler.Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);

        bundle.WinnerQuoteTotal.Should().Be(150_000_000m, "chỉ SUM winner — NCC không trúng bị loại");
    }

    [Fact]
    public async Task WinnerQuoteTotal_SingleWinner_EqualsThatSupplierQuote_SelectedSupplierIdSet()
    {
        // 1 winner (single-winner backward compat) → WinnerQuoteTotal = đúng báo giá đơn
        // vị đó + SelectedSupplierId sync = winner (handler select-winner set khi count==1).
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var pe = await SeedPeAsync(db, "PE-MW-1WIN");
        var detailId = await SeedDetailAsync(db, pe);

        var winner = await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-SOLO", 75_000_000m, isWinner: true, order: 0);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-OTHER", 80_000_000m, isWinner: false, order: 1);
        // Mô phỏng handler select-winner sync SelectedSupplierId khi đúng 1 winner.
        pe.SelectedSupplierId = winner;
        await db.SaveChangesAsync(CancellationToken.None);

        var handler = BuildQueryHandler(fix, db, AsRoles(AppRoles.Admin));
        var bundle = await handler.Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);

        bundle.WinnerQuoteTotal.Should().Be(75_000_000m, "1 winner → tổng = đúng báo giá đơn vị đó");
        bundle.SelectedSupplierId.Should().Be(winner);
    }

    [Fact]
    public async Task WinnerQuoteTotal_NoWinner_IsZero()
    {
        // Chưa chọn winner nào (mọi PES IsWinner=false) → WinnerQuoteTotal = 0 (KHÔNG
        // lẫn báo giá các NCC tham gia chưa-trúng).
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var pe = await SeedPeAsync(db, "PE-MW-NONE");
        var detailId = await SeedDetailAsync(db, pe);

        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-X", 40_000_000m, isWinner: false, order: 0);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-Y", 60_000_000m, isWinner: false, order: 1);

        var handler = BuildQueryHandler(fix, db, AsRoles(AppRoles.Admin));
        var bundle = await handler.Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);

        bundle.WinnerQuoteTotal.Should().Be(0m, "0 winner → tổng gói = 0");
    }

    [Fact]
    public async Task WinnerQuoteTotal_MultipleQuotesPerWinner_SumsAllRows()
    {
        // 1 winner nhưng có 2 detail (2 dòng báo giá thuộc cùng PES winner) → SUM cả 2
        // dòng (20 + 30 = 50tr). Verify SelectMany Details.Quotes gộp mọi dòng của winner-row.
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var pe = await SeedPeAsync(db, "PE-MW-MULTQ");

        // 2 detail rời.
        var detail1 = await SeedDetailAsync(db, pe);
        var detail2 = new PurchaseEvaluationDetail
        {
            Id = Guid.NewGuid(), PurchaseEvaluationId = pe.Id,
            GroupCode = "A.II", GroupName = "Thép", NoiDung = "Steel D10", Order = 1,
        };
        db.PurchaseEvaluationDetails.Add(detail2);
        await db.SaveChangesAsync(CancellationToken.None);

        // 1 winner PES + 2 quote (mỗi detail 1 dòng).
        var supplier = new Supplier { Id = Guid.NewGuid(), Code = "NCC-2Q", Name = "NCC 2 dòng" };
        db.Suppliers.Add(supplier);
        var pes = new PurchaseEvaluationSupplier
        {
            Id = Guid.NewGuid(), PurchaseEvaluationId = pe.Id, SupplierId = supplier.Id,
            Order = 0, IsWinner = true,
        };
        db.PurchaseEvaluationSuppliers.Add(pes);
        db.PurchaseEvaluationQuotes.Add(new PurchaseEvaluationQuote
        {
            Id = Guid.NewGuid(), PurchaseEvaluationDetailId = detail1,
            PurchaseEvaluationSupplierId = pes.Id, ThanhTien = 20_000_000m,
            IsSelected = true,   // [multi-NCC] cả 2 dòng của winner ĐƯỢC CHỌN → vào tổng
        });
        db.PurchaseEvaluationQuotes.Add(new PurchaseEvaluationQuote
        {
            Id = Guid.NewGuid(), PurchaseEvaluationDetailId = detail2.Id,
            PurchaseEvaluationSupplierId = pes.Id, ThanhTien = 30_000_000m,
            IsSelected = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        var handler = BuildQueryHandler(fix, db, AsRoles(AppRoles.Admin));
        var bundle = await handler.Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);

        bundle.WinnerQuoteTotal.Should().Be(50_000_000m, "SUM mọi dòng báo giá của winner-row qua nhiều detail");
    }

    // ===== [multi-NCC 2026-07-12 cardinality-change — NEW test (a)] =====
    // ≥2 NCC/TP cùng trúng TRÊN CÙNG 1 HẠNG MỤC (Detail) — mỗi nhà 1 giá riêng (Quote.ThanhTien).
    // winnerQuoteTotal = SUM 2 quote IsSelected CỦA hạng mục đó; NCC thứ 3 chào cùng hạng mục
    // nhưng KHÔNG được chọn (IsSelected=false) → bị loại. Đây là trục cardinality-change cốt lõi:
    // winner-truth = per-(Detail×Supplier) Quote.IsSelected, KHÔNG phải whole-supplier.
    [Fact]
    public async Task WinnerQuoteTotal_TwoNccSameDetail_SumsBothSelected_ExcludesUnselectedThird()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var pe = await SeedPeAsync(db, "PE-MW-2NCC1HM");
        var detailId = await SeedDetailAsync(db, pe);   // 1 hạng mục dùng chung 3 NCC

        // 3 NCC chào CÙNG hạng mục detailId: 2 được chọn (33 + 27) + 1 không chọn (50).
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-P1", 33_000_000m, isWinner: true, order: 0);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-P2", 27_000_000m, isWinner: true, order: 1);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-P3", 50_000_000m, isWinner: false, order: 2);

        var handler = BuildQueryHandler(fix, db, AsRoles(AppRoles.Admin));
        var bundle = await handler.Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);

        bundle.WinnerQuoteTotal.Should().Be(60_000_000m,
            "≥2 NCC trúng CÙNG 1 hạng mục → SUM 2 quote ĐƯỢC CHỌN (33+27); NCC thứ 3 không chọn bị loại");
        bundle.SelectedSupplierId.Should().BeNull("≥2 đơn vị cùng trúng → không có 1 winner duy nhất");

        // Quote của NCC thứ 3 tồn tại nhưng IsSelected=false → chứng minh loại theo IsSelected.
        var detailDto = bundle.Details.Single(d => d.Id == detailId);
        detailDto.Quotes.Should().HaveCount(3, "cả 3 NCC đều có báo giá cho hạng mục");
        detailDto.Quotes.Count(q => q.IsSelected).Should().Be(2, "đúng 2 báo giá được chọn");
    }

    // ===== [S116 anh Kiệt — SỐ ÂM báo giá] winnerQuoteTotal = SUM CÓ DẤU =====
    // Báo giá ÂM (phát-sinh-giảm / hoàn tiền NCC) là giá-trị THẬT → cộng dồn CÓ DẤU,
    // KHÔNG clamp về 0 / KHÔNG Abs / KHÔNG loại. winnerQuoteTotal (DTO :1188-1191
    // `.Where(IsSelected).Sum(ThanhTien)`) phải giữ dấu âm. Nếu ai thêm clamp/Abs →
    // 2 test dưới RED.

    [Fact]
    public async Task WinnerQuoteTotal_TwoWinnersOppositeSign_SumsWithSign()
    {
        // khác winner: NCC-POS chào +50tr (vật tư), NCC-NEG ghi −20tr (hoàn tiền / giảm
        // trừ) cùng 1 hạng mục — cả 2 ĐƯỢC CHỌN → tổng gói = 50 − 20 = 30tr.
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var pe = await SeedPeAsync(db, "PE-MW-SIGN");
        var detailId = await SeedDetailAsync(db, pe);

        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-POS", 50_000_000m, isWinner: true, order: 0);
        await SeedSupplierWithQuoteAsync(db, pe, detailId, "NCC-NEG", -20_000_000m, isWinner: true, order: 1);

        var handler = BuildQueryHandler(fix, db, AsRoles(AppRoles.Admin));
        var bundle = await handler.Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);

        bundle.WinnerQuoteTotal.Should().Be(30_000_000m,
            "SUM có dấu — báo giá âm (hoàn tiền / phát-sinh-giảm) TRỪ vào tổng, KHÔNG bị loại");
    }

    [Fact]
    public async Task WinnerQuoteTotal_SameWinnerPositiveAndNegativeLines_SumsWithSign()
    {
        // cùng winner: 1 NCC trúng với 2 dòng (2 detail) — vật tư +80tr + phát-sinh-giảm
        // −30tr → tổng = 80 − 30 = 50tr. Verify SelectMany gộp mọi dòng của winner-row
        // GIỮ dấu (mirror WinnerQuoteTotal_MultipleQuotesPerWinner nhưng 1 dòng âm).
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var pe = await SeedPeAsync(db, "PE-MW-SIGN2");

        var detail1 = await SeedDetailAsync(db, pe);
        var detail2 = new PurchaseEvaluationDetail
        {
            Id = Guid.NewGuid(), PurchaseEvaluationId = pe.Id,
            GroupCode = "A.II", GroupName = "Phát sinh", NoiDung = "Giảm trừ", Order = 1,
        };
        db.PurchaseEvaluationDetails.Add(detail2);
        await db.SaveChangesAsync(CancellationToken.None);

        var supplier = new Supplier { Id = Guid.NewGuid(), Code = "NCC-SGN", Name = "NCC 2 dòng trái dấu" };
        db.Suppliers.Add(supplier);
        var pes = new PurchaseEvaluationSupplier
        {
            Id = Guid.NewGuid(), PurchaseEvaluationId = pe.Id, SupplierId = supplier.Id,
            Order = 0, IsWinner = true,
        };
        db.PurchaseEvaluationSuppliers.Add(pes);
        db.PurchaseEvaluationQuotes.Add(new PurchaseEvaluationQuote
        {
            Id = Guid.NewGuid(), PurchaseEvaluationDetailId = detail1,
            PurchaseEvaluationSupplierId = pes.Id, ThanhTien = 80_000_000m, IsSelected = true,
        });
        db.PurchaseEvaluationQuotes.Add(new PurchaseEvaluationQuote
        {
            Id = Guid.NewGuid(), PurchaseEvaluationDetailId = detail2.Id,
            PurchaseEvaluationSupplierId = pes.Id, ThanhTien = -30_000_000m, IsSelected = true,
        });
        await db.SaveChangesAsync(CancellationToken.None);

        var handler = BuildQueryHandler(fix, db, AsRoles(AppRoles.Admin));
        var bundle = await handler.Handle(new GetPurchaseEvaluationQuery(pe.Id), CancellationToken.None);

        bundle.WinnerQuoteTotal.Should().Be(50_000_000m,
            "cùng winner 2 dòng trái dấu → SUM có dấu (80 − 30), dòng âm KHÔNG bị bỏ");
    }
}
