using ClaudeDo.Data;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.External;
using ClaudeDo.Worker.Hub;
using ClaudeDo.Worker.Tests.Infrastructure;

namespace ClaudeDo.Worker.Tests.External;

public sealed class NotesMcpToolsTests : IDisposable
{
    private readonly DbFixture _db = new();
    private readonly ClaudeDoDbContext _ctx;
    private readonly DailyNoteRepository _notes;
    private readonly NotesMcpTools _sut;
    private static readonly DateOnly Today = DateOnly.FromDateTime(DateTime.Today);

    public NotesMcpToolsTests()
    {
        _ctx = _db.CreateContext();
        _notes = new DailyNoteRepository(_ctx);
        _sut = new NotesMcpTools(_notes, new HubBroadcaster(new CapturingHubContext()));
    }

    public void Dispose() { _ctx.Dispose(); _db.Dispose(); }

    [Fact]
    public async Task AddNote_stores_it_on_today_flagged_as_agent_written()
    {
        var result = await _sut.AddNote("  Mika decided to keep the daily grouping.  ");

        Assert.True(result.Saved);
        var row = Assert.Single(await _notes.ListByDayAsync(Today));
        Assert.Equal("Mika decided to keep the daily grouping.", row.Text);
        Assert.True(row.FromAgent);
        Assert.Equal(NotesMcpTools.DailyAgentCap - 1, result.RemainingToday);
    }

    [Fact]
    public async Task AddNote_refuses_once_the_daily_cap_is_used_up_and_saves_nothing()
    {
        for (var i = 0; i < NotesMcpTools.DailyAgentCap; i++)
            await _sut.AddNote($"note {i}");

        var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.AddNote("one too many"));

        Assert.Contains("cap", ex.Message, StringComparison.OrdinalIgnoreCase);
        Assert.Equal(NotesMcpTools.DailyAgentCap, (await _notes.ListByDayAsync(Today)).Count);
    }

    [Fact]
    public async Task Notes_the_user_wrote_do_not_count_against_the_agent_cap()
    {
        for (var i = 0; i < NotesMcpTools.DailyAgentCap; i++)
            await _notes.AddAsync(Today, $"mine {i}");

        var result = await _sut.AddNote("still allowed");

        Assert.Equal(1, result.AgentNotesToday);
    }

    [Theory]
    [InlineData("")]
    [InlineData("   ")]
    public async Task AddNote_rejects_empty_text(string text) =>
        await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.AddNote(text));

    [Fact]
    public async Task AddNote_rejects_text_past_the_length_limit()
    {
        var tooLong = new string('x', NotesMcpTools.MaxLength + 1);

        var ex = await Assert.ThrowsAsync<InvalidOperationException>(() => _sut.AddNote(tooLong));

        Assert.Contains(NotesMcpTools.MaxLength.ToString(), ex.Message);
        Assert.Empty(await _notes.ListByDayAsync(Today));
    }
}
