using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Moq;
using ROLAC.API.Data;
using ROLAC.API.Data.Interceptors;
using ROLAC.API.DTOs.Giving;
using ROLAC.API.Services;
using Xunit;

namespace ROLAC.API.Tests.Services;

public class GivingCategoryServiceTests
{
    private static IHttpContextAccessor BuildAccessor(string userId = "test-user")
    {
        var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userId) };
        var ctx    = new DefaultHttpContext { User = new(new ClaimsIdentity(claims)) };
        var mock   = new Mock<IHttpContextAccessor>();
        mock.Setup(x => x.HttpContext).Returns(ctx);
        return mock.Object;
    }

    private static AppDbContext BuildDb(string userId = "test-user")
    {
        var interceptor = new AuditSaveChangesInterceptor(new ROLAC.API.Services.Logging.CurrentUserAccessor(BuildAccessor(userId)));
        return new AppDbContext(
            new DbContextOptionsBuilder<AppDbContext>()
                .UseInMemoryDatabase(Guid.NewGuid().ToString())
                .AddInterceptors(interceptor)
                .Options);
    }

    [Fact]
    public async Task CreateAsync_ReturnsId_AndDefaultsActive()
    {
        using var db = BuildDb();
        var svc = new GivingCategoryService(db);

        var id = await svc.CreateAsync(new CreateGivingCategoryRequest { Name_en = "Tithe", Name_zh = "什一" });

        var saved = await db.GivingCategories.FindAsync(id);
        Assert.NotNull(saved);
        Assert.True(saved!.IsActive);
        Assert.Equal("Tithe", saved.Name_en);
    }

    [Fact]
    public async Task GetAllAsync_ExcludesInactive_ByDefault()
    {
        using var db = BuildDb();
        var svc = new GivingCategoryService(db);
        var id1 = await svc.CreateAsync(new CreateGivingCategoryRequest { Name_en = "Active" });
        var id2 = await svc.CreateAsync(new CreateGivingCategoryRequest { Name_en = "Gone" });
        await svc.DeactivateAsync(id2);

        var active = await svc.GetAllAsync(includeInactive: false);
        var all    = await svc.GetAllAsync(includeInactive: true);

        Assert.Single(active);
        Assert.Equal(2, all.Count);
    }

    [Fact]
    public async Task DeactivateAsync_SetsIsActiveFalse()
    {
        using var db = BuildDb();
        var svc = new GivingCategoryService(db);
        var id = await svc.CreateAsync(new CreateGivingCategoryRequest { Name_en = "Temp" });

        await svc.DeactivateAsync(id);

        var saved = await db.GivingCategories.FindAsync(id);
        Assert.False(saved!.IsActive);
    }

    [Fact]
    public async Task UpdateAsync_Throws_WhenMissing()
    {
        using var db = BuildDb();
        var svc = new GivingCategoryService(db);
        await Assert.ThrowsAsync<KeyNotFoundException>(() =>
            svc.UpdateAsync(999, new UpdateGivingCategoryRequest { Name_en = "X" }));
    }

    [Fact]
    public async Task DeactivateAsync_Throws_WhenMissing()
    {
        using var db = BuildDb();
        var svc = new GivingCategoryService(db);
        await Assert.ThrowsAsync<KeyNotFoundException>(() => svc.DeactivateAsync(999));
    }
}
