using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Data.Repositories;
using Microsoft.EntityFrameworkCore;
using TaskStatus = ClaudeDo.Data.Models.TaskStatus;

namespace ClaudeDo.Data.Tests;

public sealed class UsageGateAndRunModelTests : IDisposable
{
    private readonly string _dbPath;
    private readonly ClaudeDoDbContext _ctx;

    public UsageGateAndRunModelTests()
    {
        _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_usagegate_{Guid.NewGuid():N}.db");
        var options = new DbContextOptionsBuilder<ClaudeDoDbContext>()
            .UseSqlite($"Data Source={_dbPath}")
            .Options;
        _ctx = new ClaudeDoDbContext(options);
        _ctx.Database.EnsureCreated();
    }

    public void Dispose()
    {
        _ctx.Dispose();
        foreach (var suffix in new[] { "", "-wal", "-shm" })
            try { File.Delete(_dbPath + suffix); } catch { }
    }

    [Fact]
    public async Task Fresh_db_seeds_usage_gate_defaults()
    {
        var settings = await _ctx.AppSettings.AsNoTracking().FirstAsync(s => s.Id == AppSettingsEntity.SingletonId);

        Assert.Equal(80, settings.UsageGateFiveHourPct);
        Assert.Equal(90, settings.UsageGateSevenDayPct);
    }

    [Fact]
    public async Task UpdateAsync_clamps_out_of_range_values()
    {
        var repo = new AppSettingsRepository(_ctx);
        var current = await repo.GetAsync();
        current.UsageGateFiveHourPct = -5;
        current.UsageGateSevenDayPct = 150;

        await repo.UpdateAsync(current);
        var reloaded = await repo.GetAsync();

        Assert.Equal(0, reloaded.UsageGateFiveHourPct);
        Assert.Equal(100, reloaded.UsageGateSevenDayPct);
    }

    [Fact]
    public async Task UpdateAsync_writes_through_valid_values()
    {
        var repo = new AppSettingsRepository(_ctx);
        var current = await repo.GetAsync();
        current.UsageGateFiveHourPct = 60;
        current.UsageGateSevenDayPct = 95;

        await repo.UpdateAsync(current);
        var reloaded = await repo.GetAsync();

        Assert.Equal(60, reloaded.UsageGateFiveHourPct);
        Assert.Equal(95, reloaded.UsageGateSevenDayPct);
    }

    [Fact]
    public async Task TaskRun_model_roundtrips_when_set()
    {
        var list = new ListEntity { Id = "l1", Name = "Test", CreatedAt = DateTime.UtcNow };
        var task = new TaskEntity { Id = "t1", ListId = "l1", Title = "T", Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow };
        var run = new TaskRunEntity
        {
            Id = "r1",
            TaskId = "t1",
            RunNumber = 1,
            IsRetry = false,
            Prompt = "do the thing",
            Model = "sonnet",
        };
        _ctx.Lists.Add(list);
        _ctx.Tasks.Add(task);
        _ctx.TaskRuns.Add(run);
        await _ctx.SaveChangesAsync();

        await using var freshCtx = new ClaudeDoDbContext(
            new DbContextOptionsBuilder<ClaudeDoDbContext>().UseSqlite($"Data Source={_dbPath}").Options);
        var reloaded = await freshCtx.TaskRuns.AsNoTracking().FirstAsync(r => r.Id == "r1");

        Assert.Equal("sonnet", reloaded.Model);
    }

    [Fact]
    public async Task TaskRun_model_stays_null_when_not_set()
    {
        var list = new ListEntity { Id = "l2", Name = "Test", CreatedAt = DateTime.UtcNow };
        var task = new TaskEntity { Id = "t2", ListId = "l2", Title = "T", Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow };
        var run = new TaskRunEntity
        {
            Id = "r2",
            TaskId = "t2",
            RunNumber = 1,
            IsRetry = false,
            Prompt = "do the thing",
        };
        _ctx.Lists.Add(list);
        _ctx.Tasks.Add(task);
        _ctx.TaskRuns.Add(run);
        await _ctx.SaveChangesAsync();

        await using var freshCtx = new ClaudeDoDbContext(
            new DbContextOptionsBuilder<ClaudeDoDbContext>().UseSqlite($"Data Source={_dbPath}").Options);
        var reloaded = await freshCtx.TaskRuns.AsNoTracking().FirstAsync(r => r.Id == "r2");

        Assert.Null(reloaded.Model);
    }

    [Fact]
    public async Task TaskRun_cache_token_fields_roundtrip_when_set()
    {
        var list = new ListEntity { Id = "l3", Name = "Test", CreatedAt = DateTime.UtcNow };
        var task = new TaskEntity { Id = "t3", ListId = "l3", Title = "T", Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow };
        var run = new TaskRunEntity
        {
            Id = "r3",
            TaskId = "t3",
            RunNumber = 1,
            IsRetry = false,
            Prompt = "do the thing",
            CacheReadTokens = 12345,
            CacheWriteTokens = 678,
        };
        _ctx.Lists.Add(list);
        _ctx.Tasks.Add(task);
        _ctx.TaskRuns.Add(run);
        await _ctx.SaveChangesAsync();

        await using var freshCtx = new ClaudeDoDbContext(
            new DbContextOptionsBuilder<ClaudeDoDbContext>().UseSqlite($"Data Source={_dbPath}").Options);
        var reloaded = await freshCtx.TaskRuns.AsNoTracking().FirstAsync(r => r.Id == "r3");

        Assert.Equal(12345, reloaded.CacheReadTokens);
        Assert.Equal(678, reloaded.CacheWriteTokens);
    }

    [Fact]
    public async Task TaskRun_cache_token_fields_stay_null_when_not_set()
    {
        var list = new ListEntity { Id = "l4", Name = "Test", CreatedAt = DateTime.UtcNow };
        var task = new TaskEntity { Id = "t4", ListId = "l4", Title = "T", Status = TaskStatus.Idle, CreatedAt = DateTime.UtcNow };
        var run = new TaskRunEntity
        {
            Id = "r4",
            TaskId = "t4",
            RunNumber = 1,
            IsRetry = false,
            Prompt = "do the thing",
        };
        _ctx.Lists.Add(list);
        _ctx.Tasks.Add(task);
        _ctx.TaskRuns.Add(run);
        await _ctx.SaveChangesAsync();

        await using var freshCtx = new ClaudeDoDbContext(
            new DbContextOptionsBuilder<ClaudeDoDbContext>().UseSqlite($"Data Source={_dbPath}").Options);
        var reloaded = await freshCtx.TaskRuns.AsNoTracking().FirstAsync(r => r.Id == "r4");

        Assert.Null(reloaded.CacheReadTokens);
        Assert.Null(reloaded.CacheWriteTokens);
    }
}
