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.Expense;
using ROLAC.API.Services;
using Xunit;

namespace ROLAC.API.Tests.Services;

public class ExpenseCategoryServiceTests
{
    private static AppDbContext BuildDb()
    {
        var claims = new[] { new Claim(ClaimTypes.NameIdentifier, "test-user") };
        var ctx    = new DefaultHttpContext { User = new(new ClaimsIdentity(claims)) };
        var mock   = new Mock<IHttpContextAccessor>();
        mock.Setup(x => x.HttpContext).Returns(ctx);
        return new AppDbContext(new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .AddInterceptors(new AuditSaveChangesInterceptor(new ROLAC.API.Services.Logging.CurrentUserAccessor(mock.Object))).Options);
    }

    [Fact]
    public async Task GetAll_NestsSubcategories_AndExcludesInactiveByDefault()
    {
        using var db = BuildDb();
        var svc = new ExpenseCategoryService(db);
        var gid = await svc.CreateGroupAsync(new CreateExpenseGroupRequest { Name_en = "Equipment" });
        var sid = await svc.CreateSubCategoryAsync(new CreateExpenseSubCategoryRequest { GroupId = gid, Name_en = "Purchase" });
        await svc.DeactivateSubCategoryAsync(sid);

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

        Assert.Single(active);
        Assert.Empty(active[0].SubCategories);
        Assert.Single(all[0].SubCategories);
    }

    [Fact]
    public async Task DeactivateGroup_SetsInactive()
    {
        using var db = BuildDb();
        var svc = new ExpenseCategoryService(db);
        var gid = await svc.CreateGroupAsync(new CreateExpenseGroupRequest { Name_en = "Other" });
        await svc.DeactivateGroupAsync(gid);
        Assert.Empty(await svc.GetAllAsync(includeInactive: false));
    }

    [Fact]
    public async Task UpdateGroup_Throws_WhenMissing()
    {
        using var db = BuildDb();
        var svc = new ExpenseCategoryService(db);
        await Assert.ThrowsAsync<KeyNotFoundException>(() =>
            svc.UpdateGroupAsync(999, new UpdateExpenseGroupRequest { Name_en = "X" }));
    }

    [Fact]
    public async Task CreateAndGet_RoundTrips_Form990LineId()
    {
        using var db = BuildDb();
        db.Form990ExpenseLines.Add(new ROLAC.API.Entities.Form990ExpenseLine { Id = 1, LineCode = "24", Name_en = "Other" });
        db.Form990ExpenseLines.Add(new ROLAC.API.Entities.Form990ExpenseLine { Id = 7, LineCode = "7", Name_en = "Salaries" });
        await db.SaveChangesAsync();
        var svc = new ExpenseCategoryService(db);
        var gid = await svc.CreateGroupAsync(new CreateExpenseGroupRequest { Name_en = "Personnel", Form990LineId = 1 });
        var sid = await svc.CreateSubCategoryAsync(new CreateExpenseSubCategoryRequest { GroupId = gid, Name_en = "Salary & Wages", Form990LineId = 7 });

        var all = await svc.GetAllAsync(includeInactive: true);
        var sub = all.Single(g => g.Id == gid).SubCategories.Single(s => s.Id == sid);
        Assert.Equal(7, sub.Form990LineId);
        Assert.Equal("7", sub.Form990LineCode);
        Assert.Equal(1, all.Single(g => g.Id == gid).Form990LineId);
        Assert.Equal("24", all.Single(g => g.Id == gid).Form990LineCode);
    }
}
