using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Tests.Infrastructure;

namespace ClaudeDo.Worker.Tests.Repositories;

public class AppSettingsRepositoryTests : IDisposable
{
    private readonly DbFixture _db = new();

    public void Dispose() => _db.Dispose();

    [Fact]
    public async Task GetAsync_Returns_Seeded_Defaults()
    {
        using var ctx = _db.CreateContext();
        var repo = new AppSettingsRepository(ctx);

        var row = await repo.GetAsync();

        Assert.Equal(AppSettingsEntity.SingletonId, row.Id);
        Assert.Equal("sonnet", row.DefaultModel);
        Assert.Equal(40, row.DefaultMaxTurns);
        Assert.Equal(80, row.MaxTurnsCeiling);
        Assert.Equal("auto", row.DefaultPermissionMode);
        Assert.Equal("sibling", row.WorktreeStrategy);
        Assert.Null(row.CentralWorktreeRoot);
        Assert.False(row.WorktreeAutoCleanupEnabled);
    }

    [Fact]
    public async Task UpdateAsync_Persists_MaxTurnsCeiling()
    {
        using (var ctx = _db.CreateContext())
        {
            var repo = new AppSettingsRepository(ctx);
            await repo.UpdateAsync(new AppSettingsEntity { MaxTurnsCeiling = 60 });
        }

        using var readCtx = _db.CreateContext();
        var row = await new AppSettingsRepository(readCtx).GetAsync();
        Assert.Equal(60, row.MaxTurnsCeiling);
    }

    [Fact]
    public async Task GetAsync_Backfills_Null_ModelPresets_With_Defaults()
    {
        using (var ctx = _db.CreateContext())
        {
            var repo = new AppSettingsRepository(ctx);
            // Force a row to exist with model_presets left null (the pre-upgrade state).
            await repo.UpdateAsync(new AppSettingsEntity());
        }

        using var readCtx = _db.CreateContext();
        var row = await new AppSettingsRepository(readCtx).GetAsync();

        Assert.NotNull(row.ModelPresets);
        Assert.Equal(ModelPresets.SerializeDefaults(), row.ModelPresets);

        using var rereadCtx = _db.CreateContext();
        var reread = await new AppSettingsRepository(rereadCtx).GetAsync();
        Assert.Equal(ModelPresets.SerializeDefaults(), reread.ModelPresets);
    }

    [Fact]
    public async Task UpdateAsync_Persists_And_RoundTrips()
    {
        using (var ctx = _db.CreateContext())
        {
            var repo = new AppSettingsRepository(ctx);
            await repo.UpdateAsync(new AppSettingsEntity
            {
                DefaultClaudeInstructions = "be terse",
                DefaultModel = "opus",
                DefaultMaxTurns = 42,
                DefaultPermissionMode = "acceptEdits",
                WorktreeStrategy = "central",
                CentralWorktreeRoot = "C:/worktrees",
                WorktreeAutoCleanupEnabled = true,
                WorktreeAutoCleanupDays = 14,
            });
        }

        using var readCtx = _db.CreateContext();
        var readRepo = new AppSettingsRepository(readCtx);
        var row = await readRepo.GetAsync();

        Assert.Equal("be terse", row.DefaultClaudeInstructions);
        Assert.Equal("opus", row.DefaultModel);
        Assert.Equal(42, row.DefaultMaxTurns);
        Assert.Equal("acceptEdits", row.DefaultPermissionMode);
        Assert.Equal("central", row.WorktreeStrategy);
        Assert.Equal("C:/worktrees", row.CentralWorktreeRoot);
        Assert.True(row.WorktreeAutoCleanupEnabled);
        Assert.Equal(14, row.WorktreeAutoCleanupDays);
    }

    [Fact]
    public async Task UpdateAsync_Blank_CentralRoot_Stored_As_Null()
    {
        using var ctx = _db.CreateContext();
        var repo = new AppSettingsRepository(ctx);

        await repo.UpdateAsync(new AppSettingsEntity { CentralWorktreeRoot = "   " });
        var row = await repo.GetAsync();

        Assert.Null(row.CentralWorktreeRoot);
    }

    [Fact]
    public async Task UpdateAsync_Persists_SessionSkills_Json()
    {
        using (var ctx = _db.CreateContext())
        {
            var repo = new AppSettingsRepository(ctx);
            await repo.UpdateAsync(new AppSettingsEntity
            {
                SessionSkills = "[\"ponytail\",\"ponytail-help\"]",
            });
        }

        using var readCtx = _db.CreateContext();
        var row = await new AppSettingsRepository(readCtx).GetAsync();
        Assert.Equal("[\"ponytail\",\"ponytail-help\"]", row.SessionSkills);
    }

    [Fact]
    public async Task UpdateAsync_Blank_SessionSkills_Stored_As_Null()
    {
        using (var ctx = _db.CreateContext())
        {
            var repo = new AppSettingsRepository(ctx);
            await repo.UpdateAsync(new AppSettingsEntity { SessionSkills = "[\"a\"]" });
        }

        using (var ctx = _db.CreateContext())
        {
            var repo = new AppSettingsRepository(ctx);
            await repo.UpdateAsync(new AppSettingsEntity { SessionSkills = null });
        }

        using var readCtx = _db.CreateContext();
        var row = await new AppSettingsRepository(readCtx).GetAsync();
        Assert.Null(row.SessionSkills);
    }

    [Fact]
    public async Task DailyPrepMaxTasks_defaults_to_5_and_persists()
    {
        using var ctx = _db.CreateContext();
        var repo = new AppSettingsRepository(ctx);

        var initial = await repo.GetAsync();
        Assert.Equal(5, initial.DailyPrepMaxTasks);

        initial.DailyPrepMaxTasks = 8;
        await repo.UpdateAsync(initial);

        using var readCtx = _db.CreateContext();
        var reloaded = await new AppSettingsRepository(readCtx).GetAsync();
        Assert.Equal(8, reloaded.DailyPrepMaxTasks);
    }
}
