using System.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Moq;
using ROLAC.API.Data;
using ROLAC.API.Data.Interceptors;
using ROLAC.API.DTOs.Disbursement;
using ROLAC.API.Entities;
using ROLAC.API.Services;
using ROLAC.API.Services.Logging;
using Xunit;

namespace ROLAC.API.Tests.Services;

public class ChurchProfileServiceTests
{
    // ChurchProfile is auditable, so the InMemory store rejects saves unless the
    // required CreatedBy/UpdatedBy fields are populated. Wire the same audit
    // interceptor the app uses so seeded entities save cleanly.
    private static AppDbContext NewDb()
    {
        var httpContext = new DefaultHttpContext
        {
            User = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, "test-user") })),
        };
        var httpContextAccessor = new Mock<IHttpContextAccessor>();
        httpContextAccessor.Setup(accessor => accessor.HttpContext).Returns(httpContext);
        return new AppDbContext(new DbContextOptionsBuilder<AppDbContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .ConfigureWarnings(warnings => warnings.Ignore(InMemoryEventId.TransactionIgnoredWarning))
            .AddInterceptors(new AuditSaveChangesInterceptor(new CurrentUserAccessor(httpContextAccessor.Object)))
            .Options);
    }

    private static UpdateChurchProfileRequest Req(
        string provider = "Claude", string? claudeKey = null, string? geminiKey = null,
        string? claudeModel = "m", string? geminiModel = "m") =>
        new()
        {
            Name = "C", NextCheckNumber = 1001, AiProvider = provider,
            ClaudeModel = claudeModel, GeminiModel = geminiModel,
            ClaudeApiKey = claudeKey, GeminiApiKey = geminiKey,
        };

    [Fact]
    public async Task GetAsync_masks_stored_api_keys()
    {
        using var db = NewDb();
        db.ChurchProfiles.Add(new ChurchProfile
        {
            Name = "C", ClaudeApiKey = "sk-ant-abcd1234", GeminiApiKey = "AIzaXYZ9876",
        });
        await db.SaveChangesAsync();

        var dto = await new ChurchProfileService(db).GetAsync();

        Assert.Equal("••••••1234", dto.ClaudeApiKeyMasked);
        Assert.Equal("••••••9876", dto.GeminiApiKeyMasked);
    }

    [Fact]
    public async Task UpdateAsync_blank_key_keeps_existing()
    {
        using var db = NewDb();
        db.ChurchProfiles.Add(new ChurchProfile { Name = "C", ClaudeApiKey = "sk-keep-0001" });
        await db.SaveChangesAsync();

        await new ChurchProfileService(db).UpdateAsync(Req(claudeKey: null));

        var p = await db.ChurchProfiles.FirstAsync();
        Assert.Equal("sk-keep-0001", p.ClaudeApiKey);
    }

    [Fact]
    public async Task UpdateAsync_nonblank_key_replaces()
    {
        using var db = NewDb();
        db.ChurchProfiles.Add(new ChurchProfile { Name = "C", ClaudeApiKey = "sk-keep-0001" });
        await db.SaveChangesAsync();

        await new ChurchProfileService(db).UpdateAsync(Req(claudeKey: "sk-new-9999"));

        var p = await db.ChurchProfiles.FirstAsync();
        Assert.Equal("sk-new-9999", p.ClaudeApiKey);
    }

    [Fact]
    public async Task UpdateAsync_sets_provider_and_models()
    {
        using var db = NewDb();
        db.ChurchProfiles.Add(new ChurchProfile { Name = "C" });
        await db.SaveChangesAsync();

        await new ChurchProfileService(db).UpdateAsync(
            Req(provider: "Gemini", claudeModel: "claude-x", geminiModel: "gemini-y"));

        var p = await db.ChurchProfiles.FirstAsync();
        Assert.Equal("Gemini", p.AiProvider);
        Assert.Equal("claude-x", p.ClaudeModel);
        Assert.Equal("gemini-y", p.GeminiModel);
    }
}
