using ClaudeDo.Data;
using ClaudeDo.Data.Repositories;
using ClaudeDo.Worker.Report;
using ClaudeDo.Worker.Report.Interfaces;
using ClaudeDo.Worker.Runner;
using ClaudeDo.Worker.Tests.Infrastructure;
using Microsoft.Extensions.Logging.Abstractions;

namespace ClaudeDo.Worker.Tests.Report;

public class WeekReportServiceTests : IDisposable
{
    private readonly DbFixture _db = new();
    public void Dispose() => _db.Dispose();

    private static readonly DateOnly Start = new(2026, 5, 28);
    private static readonly DateOnly End = new(2026, 6, 3);

    private sealed class FakeReader : IClaudeHistoryReader
    {
        public IReadOnlyList<RepoActivity> Result = Array.Empty<RepoActivity>();
        public Task<IReadOnlyList<RepoActivity>> ReadAsync(
            DateOnly s, DateOnly e, IReadOnlyList<string> ex, CancellationToken ct) => Task.FromResult(Result);
    }

    private sealed class FakeClaude : IClaudeProcess
    {
        public int Calls;
        public RunResult Next = new() { ExitCode = 0, ResultMarkdown = "## Bericht" };
        public Task<RunResult> RunAsync(IReadOnlyList<string> args, string prompt, string wd, Func<string, Task> onLine, CancellationToken ct)
        { Calls++; return Task.FromResult(Next); }
    }

    private WeekReportService Make(FakeReader reader, FakeClaude claude) =>
        new(reader, _db.CreateFactory(), claude, NullLogger<WeekReportService>.Instance);

    [Fact]
    public async Task EmptyWindow_ProducesNoActivityReport_WithoutCallingClaude()
    {
        var claude = new FakeClaude();
        var svc = Make(new FakeReader(), claude);

        var md = await svc.GenerateAsync(Start, End);

        Assert.Equal(0, claude.Calls);
        Assert.Contains("No activity", md);
        using var ctx = _db.CreateContext();
        Assert.NotNull(await new WeekReportRepository(ctx).GetByRangeAsync(Start, End));
    }

    [Fact]
    public async Task SuccessPath_StoresAndReturnsClaudeMarkdown()
    {
        var repo = new RepoActivity { RepoPath = @"C:\Dev\App" };
        var day = new DayActivity { Date = new DateOnly(2026, 6, 1) };
        day.Prompts.Add("Add login");
        repo.Days.Add(day);
        var claude = new FakeClaude { Next = new RunResult { ExitCode = 0, ResultMarkdown = "## Bericht\n- Habe Login umgesetzt" } };
        var svc = Make(new FakeReader { Result = new[] { repo } }, claude);

        var md = await svc.GenerateAsync(Start, End);

        Assert.Equal(1, claude.Calls);
        Assert.Contains("Habe Login umgesetzt", md);
        Assert.Equal(md, await svc.GetStoredAsync(Start, End));
    }

    [Fact]
    public async Task ClaudeFailure_Throws_AndDoesNotStore()
    {
        var repo = new RepoActivity { RepoPath = @"C:\Dev\App" };
        var day = new DayActivity { Date = new DateOnly(2026, 6, 1) };
        day.Prompts.Add("x");
        repo.Days.Add(day);
        var claude = new FakeClaude { Next = new RunResult { ExitCode = 1, ErrorMarkdown = "boom" } };
        var svc = Make(new FakeReader { Result = new[] { repo } }, claude);

        await Assert.ThrowsAsync<InvalidOperationException>(() => svc.GenerateAsync(Start, End));
        Assert.Null(await svc.GetStoredAsync(Start, End));
    }
}
