using ClaudeDo.Data;
using ClaudeDo.Data.Models;
using ClaudeDo.Localization;
using ClaudeDo.Ui.Localization;
using ClaudeDo.Ui.Services;
using ClaudeDo.Ui.ViewModels.Islands;
using Microsoft.EntityFrameworkCore;

namespace ClaudeDo.Ui.Tests.ViewModels;

// Polish-Audit 2026-08-20 #3: a list created by a running MCP session broadcasts ListUpdated with
// the raw list id, which RefreshRowAsync had no add-branch for — the list stayed invisible until
// the next app restart. And a reconnect only refreshed counts, missing offline list changes.
public class ListsIslandListUpdatedTests : IDisposable
{
    private readonly string _dbPath;

    public ListsIslandListUpdatedTests()
    {
        _dbPath = Path.Combine(Path.GetTempPath(), $"claudedo_lists_updated_test_{Guid.NewGuid():N}.db");
        using var ctx = NewContext();
        ctx.Database.EnsureCreated();

        var dir = AppContext.BaseDirectory;
        while (dir is not null && !Directory.Exists(Path.Combine(dir, "src", "ClaudeDo.Localization", "locales")))
            dir = Path.GetDirectoryName(dir);
        Loc.Current = new Localizer(
            LocaleStore.Load(Path.Combine(dir!, "src", "ClaudeDo.Localization", "locales")), "en");
    }

    public void Dispose()
    {
        try { File.Delete(_dbPath); } catch { }
        try { File.Delete(_dbPath + "-wal"); } catch { }
        try { File.Delete(_dbPath + "-shm"); } catch { }
    }

    private ClaudeDoDbContext NewContext()
    {
        var opts = new DbContextOptionsBuilder<ClaudeDoDbContext>()
            .UseSqlite($"Data Source={_dbPath}")
            .Options;
        return new ClaudeDoDbContext(opts);
    }


    private sealed class FakeWorker : StubWorkerClient
    {
        public override bool IsConnected => true;
    }

    private async Task SeedListAsync(string id, string name, int sortOrder)
    {
        await using var db = NewContext();
        db.Lists.Add(new ListEntity { Id = id, Name = name, SortOrder = sortOrder, CreatedAt = DateTime.UtcNow });
        await db.SaveChangesAsync();
    }

    [Fact]
    public async Task ListUpdated_ForAnIdNotYetLoaded_AddsItToUserLists()
    {
        await SeedListAsync("existing", "Existing", sortOrder: 0);

        var worker = new FakeWorker();
        var vm = new ListsIslandViewModel(new TestDbFactory(NewContext), worker: worker);
        await vm.LoadAsync();
        Assert.Single(vm.UserLists);

        // A running MCP session creates the list after our initial load — the worker broadcasts
        // the raw db id, never the "user:" prefixed row id.
        await SeedListAsync("new-from-mcp", "Created via MCP", sortOrder: 1);
        worker.RaiseListUpdated("new-from-mcp");
        await Task.Delay(100);

        Assert.Equal(2, vm.UserLists.Count);
        Assert.Contains(vm.UserLists, r => r.Id == "user:new-from-mcp" && r.Name == "Created via MCP");
        // Inserted at its DB sort position (after "existing"), not just appended — same relative
        // order as if it had been present since LoadAsync.
        Assert.Equal("user:existing", vm.UserLists[0].Id);
        Assert.Equal("user:new-from-mcp", vm.UserLists[1].Id);
        Assert.Contains(vm.Items, r => r.Id == "user:new-from-mcp");
    }

    [Fact]
    public async Task ListUpdated_ForAnIdMissingFromTheDbToo_DoesNothingAndDoesNotThrow()
    {
        var worker = new FakeWorker();
        var vm = new ListsIslandViewModel(new TestDbFactory(NewContext), worker: worker);
        await vm.LoadAsync();
        var countBefore = vm.UserLists.Count;

        worker.RaiseListUpdated("ghost-id");
        await Task.Delay(100);

        Assert.Equal(countBefore, vm.UserLists.Count);
    }

    [Fact]
    public async Task ConnectionRestored_FullyReloadsLists_AndKeepsSelectionIfStillPresent()
    {
        await SeedListAsync("keep-me", "Keep Me", sortOrder: 0);

        var worker = new FakeWorker();
        var vm = new ListsIslandViewModel(new TestDbFactory(NewContext), worker: worker);
        await vm.LoadAsync();
        var toSelect = vm.UserLists.Single(r => r.Id == "user:keep-me");
        vm.SelectedList = toSelect;

        // A list created while offline — the reconnect must pick it up, unlike a bare count refresh.
        await SeedListAsync("came-in-offline", "Came In Offline", sortOrder: 1);

        worker.RaiseConnectionRestored();
        await Task.Delay(150);

        Assert.Contains(vm.UserLists, r => r.Id == "user:came-in-offline");
        Assert.NotNull(vm.SelectedList);
        Assert.Equal("user:keep-me", vm.SelectedList!.Id);
    }
}
