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

namespace ClaudeDo.Data.Tests;

// A list working dir stored with a trailing separator ("C:\repo\") breaks every consumer that
// puts it on a Windows command line: the token's closing quote is escaped by the backslash, so
// "C:\repo\" parses as C:\repo" and the arg (or every arg after it) is corrupted. Observed twice
// -- ConPTY list handlers (2026-08-06) and "Open in terminal" (2026-08-10). Normalizing on write
// is the only place that covers all writers (UI, repo import, hub, MCP).
public sealed class WorkingDirNormalizationTests : IDisposable
{
    private readonly string _dbPath;
    private readonly ClaudeDoDbContext _ctx;

    public WorkingDirNormalizationTests()
    {
        _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_workdir_{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();
        try { File.Delete(_dbPath); } catch { }
        try { File.Delete(_dbPath + "-wal"); } catch { }
        try { File.Delete(_dbPath + "-shm"); } catch { }
    }

    [Theory]
    [InlineData(@"C:\Dev\Tests\StaplerTracking\", @"C:\Dev\Tests\StaplerTracking")]
    [InlineData(@"C:\Dev\Tests\StaplerTracking\\", @"C:\Dev\Tests\StaplerTracking")]
    [InlineData("C:/Dev/Tests/StaplerTracking/", "C:/Dev/Tests/StaplerTracking")]
    [InlineData(@"C:\Dev\Tests\StaplerTracking", @"C:\Dev\Tests\StaplerTracking")]
    public void TrimTrailingSeparator_strips_trailing_separators(string input, string expected) =>
        Assert.Equal(expected, Paths.TrimTrailingSeparator(input));

    // A bare drive root IS its trailing separator -- "C:" without it means "current dir on C:",
    // a different location. Same for the unix root.
    [Theory]
    [InlineData(@"C:\")]
    [InlineData("/")]
    public void TrimTrailingSeparator_leaves_a_bare_root_alone(string root) =>
        Assert.Equal(root, Paths.TrimTrailingSeparator(root));

    [Theory]
    [InlineData(null)]
    [InlineData("")]
    [InlineData("   ")]
    public void TrimTrailingSeparator_passes_blank_through(string? input) =>
        Assert.Equal(input, Paths.TrimTrailingSeparator(input));

    [Fact]
    public async Task AddAsync_normalizes_a_trailing_separator()
    {
        var repo = new ListRepository(_ctx);
        await repo.AddAsync(new ListEntity
        {
            Id = "l1",
            Name = "Stapler Tracking",
            WorkingDir = @"C:\Dev\Tests\StaplerTracking\",
            CreatedAt = DateTime.UtcNow,
        });

        var stored = await repo.GetByIdAsync("l1");
        Assert.Equal(@"C:\Dev\Tests\StaplerTracking", stored!.WorkingDir);
    }

    [Fact]
    public async Task UpdateAsync_normalizes_a_trailing_separator()
    {
        var repo = new ListRepository(_ctx);
        await repo.AddAsync(new ListEntity { Id = "l1", Name = "L", CreatedAt = DateTime.UtcNow });

        var entity = await repo.GetByIdAsync("l1");
        entity!.WorkingDir = @"C:\Dev\Repos\Bandel.Hub\";
        await repo.UpdateAsync(entity);

        var stored = await repo.GetByIdAsync("l1");
        Assert.Equal(@"C:\Dev\Repos\Bandel.Hub", stored!.WorkingDir);
    }

    [Fact]
    public async Task AddAsync_leaves_a_null_working_dir_null()
    {
        var repo = new ListRepository(_ctx);
        await repo.AddAsync(new ListEntity { Id = "l1", Name = "L", CreatedAt = DateTime.UtcNow });

        var stored = await repo.GetByIdAsync("l1");
        Assert.Null(stored!.WorkingDir);
    }
}
