using Microsoft.EntityFrameworkCore;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Master.Projects;
using SolutionErp.Domain.Master;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// [Fix UAT 2026-07-23 — dự án trùng do khác dấu-cách trong mã: "FLOCK 01" vs "FLOCK01"]
// Owner (chốt): LƯU chỉ cắt đầu/cuối (Trim) — giữ mã như người dùng gõ; nhưng KIỂM-TRÙNG
// bỏ-qua-dấu-cách để chặn tạo/sửa thành bản trùng chỉ khác khoảng trắng (chống tái phát
// sau khi gom dữ liệu). Dupe-check là bug-class guard → test-before (docs/rules.md §7).
// Handlers chỉ cần IApplicationDbContext → new trực tiếp với fix.Db.
public class ProjectCodeNormalizationTests
{
    private static CreateProjectCommand NewProject(string code, string name = "Dự án") =>
        new(code, name, null, null, null, null, null, null, null, null, null);

    [Fact]
    public async Task CreateProject_SpaceVariantOfExistingCode_ThrowsConflict()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;
        db.Projects.Add(new Project { Id = Guid.NewGuid(), Code = "FLOCK01", Name = "FLOCK01" });
        await db.SaveChangesAsync(CancellationToken.None);

        // "FLOCK 01" (dấu cách giữa) == "FLOCK01" sau khi bỏ dấu cách → phải chặn.
        var act = async () => await new CreateProjectCommandHandler(db)
            .Handle(NewProject("FLOCK 01"), CancellationToken.None);

        await act.Should().ThrowAsync<ConflictException>(
            "kiểm-trùng bỏ-qua-dấu-cách phải bắt bản trùng chỉ khác khoảng trắng");
        (await db.Projects.CountAsync()).Should().Be(1, "không tạo thêm bản trùng dấu-cách");
    }

    [Fact]
    public async Task CreateProject_TrimsLeadingTrailingWhitespace_ButKeepsInternalSpace()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;

        var id = await new CreateProjectCommandHandler(db)
            .Handle(NewProject("  CAL 02  "), CancellationToken.None);

        var saved = await db.Projects.FirstAsync(x => x.Id == id);
        // Owner chọn "chỉ cắt đầu/cuối": Trim bỏ khoảng trắng đầu/cuối, GIỮ dấu cách giữa.
        saved.Code.Should().Be("CAL 02");
    }

    [Fact]
    public async Task CreateProject_GenuinelyNewCode_Succeeds()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;
        db.Projects.Add(new Project { Id = Guid.NewGuid(), Code = "FLOCK01", Name = "FLOCK01" });
        await db.SaveChangesAsync(CancellationToken.None);

        var act = async () => await new CreateProjectCommandHandler(db)
            .Handle(NewProject("SOV01"), CancellationToken.None);

        await act.Should().NotThrowAsync("mã mới hoàn toàn không đụng bản nào");
        (await db.Projects.CountAsync()).Should().Be(2);
    }

    [Fact]
    public async Task UpdateProject_ToSpaceVariantOfAnotherCode_ThrowsConflict()
    {
        using var fix = new SqliteDbFixture();
        var db = fix.Db;
        db.Projects.Add(new Project { Id = Guid.NewGuid(), Code = "FLOCK01", Name = "FLOCK01" });
        var otherId = Guid.NewGuid();
        db.Projects.Add(new Project { Id = otherId, Code = "TARGET", Name = "Khác" });
        await db.SaveChangesAsync(CancellationToken.None);

        // Đổi mã "TARGET" → "FLOCK 01" (trùng "FLOCK01" bỏ dấu cách) → phải chặn.
        var act = async () => await new UpdateProjectCommandHandler(db)
            .Handle(new UpdateProjectCommand(otherId, "FLOCK 01", "Khác",
                null, null, null, null, null, null, null, null, null), CancellationToken.None);

        await act.Should().ThrowAsync<ConflictException>(
            "sửa sang biến thể dấu-cách của mã đã tồn tại cũng phải bị chặn");
    }
}
