using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using SolutionErp.Application.Common.Exceptions;
using SolutionErp.Application.Hrm;
using SolutionErp.Application.Hrm.Services;
using SolutionErp.Domain.Hrm;
using SolutionErp.Domain.Identity;
using SolutionErp.Infrastructure.Services;
using SolutionErp.Infrastructure.Tests.Common;

namespace SolutionErp.Infrastructure.Tests.Application;

// Plan G-H1 Phase 1.5 Test Bundle 2 (S34) — CreateEmployeeProfileCommand handler.
// 4 [Fact] cover: success / duplicate-active / duplicate-soft-deleted / user-not-found.
//
// Mirror CreateContractCommandApplicableTypeTests (BW5 ConflictException pattern):
//   IdentityFixture + handler.Handle() direct invocation (skip MediatR pipeline,
//   FluentValidator chỉ field-level constraint không tham gia cross-table check).
//
// IMPORTANT spec mismatch documented (S34 Test Bundle):
//   - Spec Fact 3 mention "AfterSoftDelete allows new profile for same userId".
//   - Code (EmployeeFeatures.cs:158-163) check existing KHÔNG filter IsDeleted →
//     soft-deleted profile vẫn block new profile + throw với MESSAGE KHÁC
//     ("đã xoá mềm. Cần khôi phục thay vì tạo mới.").
//   - Test theo CODE thực tế (single source of truth): expect ConflictException
//     với discriminator message khác giữa 2 case (active vs soft-deleted).
//   - Em main confirm khi review test bundle: spec → code drift, code chuẩn.
public class CreateEmployeeProfileCommandTests
{
    private static CreateEmployeeProfileCommandHandler CreateHandler(
        IdentityFixture fix, FixedDateTime dt)
    {
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var um = fix.Services.GetRequiredService<UserManager<User>>();
        var codeGen = new EmployeeCodeGenerator(db, dt);
        return new CreateEmployeeProfileCommandHandler(db, codeGen, um);
    }

    // ===== Spec Fact 1: First profile for user — happy path =====

    [Fact]
    public async Task Create_FirstProfileForUser_ReturnsId_PersistsEntity()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var dt = new FixedDateTime(new DateTime(2026, 6, 15, 0, 0, 0, DateTimeKind.Utc));

        var user = await fix.CreateUserAsync("nv001@test.local", "Nguyễn Văn A",
            departmentId: null, roles: Array.Empty<string>());

        var handler = CreateHandler(fix, dt);
        var cmd = new CreateEmployeeProfileCommand(UserId: user.Id);

        var id = await handler.Handle(cmd, CancellationToken.None);

        // Returns Guid not empty.
        id.Should().NotBeEmpty();

        // Persists entity với EmployeeCode "NV/2026/0001" (first call of year).
        var entity = await db.EmployeeProfiles.AsNoTracking()
            .FirstOrDefaultAsync(x => x.Id == id);
        entity.Should().NotBeNull();
        entity!.UserId.Should().Be(user.Id);
        entity.EmployeeCode.Should().Be("NV/2026/0001");
        entity.EmployeeStatus.Should().Be(EmployeeStatus.Active);  // Default per command record.
        entity.Nationality.Should().Be("Việt Nam");  // Default trong handler line 184.
    }

    // ===== Spec Fact 2: Duplicate userId (active existing) — ConflictException =====

    [Fact]
    public async Task Create_DuplicateUserId_ThrowsConflictException()
    {
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var dt = new FixedDateTime(new DateTime(2026, 6, 15, 0, 0, 0, DateTimeKind.Utc));

        var user = await fix.CreateUserAsync("nv002@test.local", "Nguyễn Văn B",
            departmentId: null, roles: Array.Empty<string>());

        // Seed 1 active profile (KHÔNG soft-deleted).
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = user.Id,
            EmployeeCode = "NV/2026/9001",
            EmployeeStatus = EmployeeStatus.Active,
            IsDeleted = false,
        });
        await db.SaveChangesAsync();

        var handler = CreateHandler(fix, dt);
        var cmd = new CreateEmployeeProfileCommand(UserId: user.Id);

        var act = async () => await handler.Handle(cmd, CancellationToken.None);

        // Code line 162-163 throw active message — discriminator vs soft-deleted.
        await act.Should().ThrowAsync<ConflictException>()
            .WithMessage("*đã có hồ sơ NV*mỗi user chỉ được 1 hồ sơ*");
    }

    // ===== Spec Fact 3: Soft-deleted existing — BLOCKED (code behavior, NOT spec) =====

    [Fact]
    public async Task Create_AfterSoftDelete_ThrowsConflictWithRestoreMessage()
    {
        // SPEC MISMATCH: spec mention "allows new profile" — code chặn lại + throw
        // với message "đã xoá mềm. Cần khôi phục". Test theo CODE (line 158-163).
        // Em main review: behavior này CORRECT vì admin biết user từng có profile
        // → cần khôi phục thay vì tạo mới (Phase 1.5 restore flow defer).
        using var fix = new IdentityFixture();
        var db = fix.Services.GetRequiredService<TestApplicationDbContext>();
        var dt = new FixedDateTime(new DateTime(2026, 6, 15, 0, 0, 0, DateTimeKind.Utc));

        var user = await fix.CreateUserAsync("nv003@test.local", "Nguyễn Văn C",
            departmentId: null, roles: Array.Empty<string>());

        // Seed 1 soft-deleted profile.
        db.EmployeeProfiles.Add(new EmployeeProfile
        {
            Id = Guid.NewGuid(),
            UserId = user.Id,
            EmployeeCode = "NV/2026/9003",
            EmployeeStatus = EmployeeStatus.Resigned,
            IsDeleted = true,
            DeletedAt = new DateTime(2026, 3, 1, 0, 0, 0, DateTimeKind.Utc),
            DeletedBy = Guid.NewGuid(),
        });
        await db.SaveChangesAsync();

        var handler = CreateHandler(fix, dt);
        var cmd = new CreateEmployeeProfileCommand(UserId: user.Id);

        var act = async () => await handler.Handle(cmd, CancellationToken.None);

        // Code line 161-162 throw soft-delete message — discriminator vs active.
        await act.Should().ThrowAsync<ConflictException>()
            .WithMessage("*đã xoá mềm*khôi phục thay vì tạo mới*");
    }

    // ===== Spec Fact 4: User not found — NotFoundException =====

    [Fact]
    public async Task Create_UserNotFound_ThrowsNotFoundException()
    {
        using var fix = new IdentityFixture();
        var dt = new FixedDateTime(new DateTime(2026, 6, 15, 0, 0, 0, DateTimeKind.Utc));

        var handler = CreateHandler(fix, dt);
        var randomUserId = Guid.NewGuid();  // KHÔNG có trong Users table.
        var cmd = new CreateEmployeeProfileCommand(UserId: randomUserId);

        var act = async () => await handler.Handle(cmd, CancellationToken.None);

        // Code line 153-154 throw NotFoundException qua userManager.FindByIdAsync null.
        await act.Should().ThrowAsync<NotFoundException>()
            .WithMessage($"*User*{randomUserId}*không tồn tại*");
    }
}
