using Microsoft.Extensions.DependencyInjection;
using SolutionErp.Application.Hrm;
using SolutionErp.Domain.Hrm;
using SolutionErp.Domain.Master;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// Plan G-H1 Phase 1.5 Test Bundle 3 (S34) — ListEmployeesQuery filter + paging.
// 3 [Fact] cover: status filter / department filter / search by code.
//
// Mirror existing List query test pattern. JOIN Users + Departments LEFT (per
// EmployeeFeatures.cs:571-575) → seed cả Department entity + User.DepartmentId
// để test department filter chuẩn xác.
//
// Search: EmployeeCode.Contains(s) || FullName.Contains(s) — use "0001" để match
// chính xác 1 row (avoid Contains("000") match cả 0010).
public class ListEmployeesQueryTests
{
    private static ListEmployeesQueryHandler CreateHandler(IdentityFixture fix)
    {
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        return new ListEmployeesQueryHandler(db);
    }

    // ===== Spec Fact 1: Filter by status — only matching =====

    [Fact]
    public async Task List_FilterByStatus_ReturnsOnlyMatching()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        // Seed 3 user + 3 profile: 2 Active + 1 Resigned.
        var user1 = await fix.CreateUserAsync("nv-active1@test.local", "Active 1",
            departmentId: null, roles: Array.Empty<string>());
        var user2 = await fix.CreateUserAsync("nv-active2@test.local", "Active 2",
            departmentId: null, roles: Array.Empty<string>());
        var user3 = await fix.CreateUserAsync("nv-resigned@test.local", "Resigned",
            departmentId: null, roles: Array.Empty<string>());

        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = user1.Id,
            EmployeeCode = "NV/2026/0001",
            EmployeeStatus = EmployeeStatus.Active,
        });
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = user2.Id,
            EmployeeCode = "NV/2026/0002",
            EmployeeStatus = EmployeeStatus.Active,
        });
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = user3.Id,
            EmployeeCode = "NV/2026/0003",
            EmployeeStatus = EmployeeStatus.Resigned,
        });
        await db.SaveChangesAsync();

        var handler = CreateHandler(fix);
        var query = new ListEmployeesQuery(Status: EmployeeStatus.Resigned);

        var result = await handler.Handle(query, CancellationToken.None);

        // Chỉ 1 row Resigned match.
        result.Items.Should().HaveCount(1);
        result.Items[0].Status.Should().Be(EmployeeStatus.Resigned);
        result.Items[0].EmployeeCode.Should().Be("NV/2026/0003");
        result.Total.Should().Be(1);
    }

    // ===== Spec Fact 2: Filter by departmentId — only matching =====

    [Fact]
    public async Task List_FilterByDepartmentId_ReturnsOnlyMatching()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        // Seed 2 department (A, B).
        var deptA = new Department { Id = Guid.NewGuid(), Code = "DPT-A", Name = "Phòng A" };
        var deptB = new Department { Id = Guid.NewGuid(), Code = "DPT-B", Name = "Phòng B" };
        db.Departments.Add(deptA);
        db.Departments.Add(deptB);
        await db.SaveChangesAsync();

        // Seed 3 user: 2 trong A + 1 trong B.
        var userA1 = await fix.CreateUserAsync("nv-a1@test.local", "User A1",
            departmentId: deptA.Id, roles: Array.Empty<string>());
        var userA2 = await fix.CreateUserAsync("nv-a2@test.local", "User A2",
            departmentId: deptA.Id, roles: Array.Empty<string>());
        var userB1 = await fix.CreateUserAsync("nv-b1@test.local", "User B1",
            departmentId: deptB.Id, roles: Array.Empty<string>());

        // Seed 3 EmployeeProfile.
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = userA1.Id,
            EmployeeCode = "NV/2026/0001",
            EmployeeStatus = EmployeeStatus.Active,
        });
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = userA2.Id,
            EmployeeCode = "NV/2026/0002",
            EmployeeStatus = EmployeeStatus.Active,
        });
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = userB1.Id,
            EmployeeCode = "NV/2026/0003",
            EmployeeStatus = EmployeeStatus.Active,
        });
        await db.SaveChangesAsync();

        var handler = CreateHandler(fix);
        var query = new ListEmployeesQuery(DepartmentId: deptA.Id);

        var result = await handler.Handle(query, CancellationToken.None);

        // 2 user trong dept A — chính xác 2 row.
        result.Items.Should().HaveCount(2);
        result.Items.Should().OnlyContain(x => x.DepartmentId == deptA.Id);
        result.Items.Should().OnlyContain(x => x.DepartmentName == "Phòng A");
        result.Total.Should().Be(2);
    }

    // ===== Spec Fact 3: Search by partial EmployeeCode — distinguishing match =====

    [Fact]
    public async Task List_SearchByCode_MatchesPartialEmployeeCode()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();

        // Seed 3 profile với code 0001 / 0002 / 0010.
        var user1 = await fix.CreateUserAsync("nv-s1@test.local", "User Search 1",
            departmentId: null, roles: Array.Empty<string>());
        var user2 = await fix.CreateUserAsync("nv-s2@test.local", "User Search 2",
            departmentId: null, roles: Array.Empty<string>());
        var user3 = await fix.CreateUserAsync("nv-s10@test.local", "User Search 10",
            departmentId: null, roles: Array.Empty<string>());

        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = user1.Id,
            EmployeeCode = "NV/2026/0001",
            EmployeeStatus = EmployeeStatus.Active,
        });
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = user2.Id,
            EmployeeCode = "NV/2026/0002",
            EmployeeStatus = EmployeeStatus.Active,
        });
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = user3.Id,
            EmployeeCode = "NV/2026/0010",
            EmployeeStatus = EmployeeStatus.Active,
        });
        await db.SaveChangesAsync();

        var handler = CreateHandler(fix);
        // Search "0001" → unique match chỉ "NV/2026/0001" (avoid Contains("000")
        // match cả 0010 — per spec Adjust note).
        var query = new ListEmployeesQuery() { Search = "0001" };

        var result = await handler.Handle(query, CancellationToken.None);

        result.Items.Should().HaveCount(1);
        result.Items[0].EmployeeCode.Should().Be("NV/2026/0001");
        result.Total.Should().Be(1);
    }
}
