using ClaudeDo.Worker.Usage.TokenTracker;

namespace ClaudeDo.Worker.Tests.Usage.TokenTracker;

public sealed class TokenTrackerExportParserTests
{
    private static readonly DateTime FetchedAt = new(2026, 8, 24, 9, 0, 0, DateTimeKind.Utc);

    [Fact]
    public void Parse_ReadsAllSessions()
    {
        var export = TokenTrackerExportParser.Parse(TokenTrackerFixtures.SessionsJson, FetchedAt);

        Assert.NotNull(export);
        Assert.Equal(4, export!.Sessions.Count);
        Assert.Equal(11, export.FormatVersion);
        Assert.Equal(FetchedAt, export.FetchedAtUtc);
    }

    [Fact]
    public void Parse_MapsTokenFieldsAndCost()
    {
        var export = TokenTrackerExportParser.Parse(TokenTrackerFixtures.SessionsJson, FetchedAt)!;
        var first = export.Sessions.Single(s => s.SessionHash == "b24babcbf2b615730459773f");

        Assert.Equal("claude-opus-5", first.Model);
        Assert.Equal(100, first.InputTokens);
        Assert.Equal(400, first.OutputTokens);
        Assert.Equal(900, first.CacheReadTokens);
        Assert.Equal(50, first.CacheCreationTokens);
        Assert.Equal(2.5, first.CostUsd);
        Assert.Equal(4, first.Turns);
        Assert.Equal(1, first.RetryTurns);
        Assert.True(first.Productive);
        Assert.True(first.OneShot);
    }

    [Fact]
    public void Parse_UnsupportedVersion_StillParsesButReportsIt()
    {
        var export = TokenTrackerExportParser.Parse(TokenTrackerFixtures.UnsupportedVersionJson, FetchedAt);

        Assert.NotNull(export);
        Assert.Equal(99, export!.FormatVersion);
    }

    [Fact]
    public void Parse_MissingFields_DefaultToZero()
    {
        const string json = """
        { "sessions": [ { "version": 11, "session_hash": "abc", "source": "claude",
                          "model": "claude-opus-5", "started_at": "2026-08-20T10:00:00.000Z" } ] }
        """;

        var session = TokenTrackerExportParser.Parse(json, FetchedAt)!.Sessions.Single();

        Assert.Equal(0, session.InputTokens);
        Assert.Equal(0, session.CostUsd);
        Assert.Equal(0, session.RetryTurns);
        Assert.False(session.Productive);
    }

    [Fact]
    public void Parse_SkipsRowsWithoutHashOrTimestamp()
    {
        const string json = """
        { "sessions": [ { "version": 11, "source": "claude", "model": "m" },
                        { "version": 11, "session_hash": "abc", "source": "claude", "model": "m" },
                        { "version": 11, "session_hash": "def", "source": "claude", "model": "m",
                          "started_at": "2026-08-20T10:00:00.000Z" } ] }
        """;

        var export = TokenTrackerExportParser.Parse(json, FetchedAt);

        Assert.Single(export!.Sessions);
        Assert.Equal("def", export.Sessions[0].SessionHash);
    }

    [Fact]
    public void Parse_SkipsNonClaudeSources()
    {
        const string json = """
        { "sessions": [ { "version": 11, "session_hash": "abc", "source": "codex", "model": "gpt",
                          "started_at": "2026-08-20T10:00:00.000Z" } ] }
        """;

        Assert.Empty(TokenTrackerExportParser.Parse(json, FetchedAt)!.Sessions);
    }

    [Theory]
    [InlineData("")]
    [InlineData("   ")]
    [InlineData("not json at all")]
    [InlineData("[1,2,3]")]
    [InlineData("{\"available\": false}")]
    public void Parse_Garbage_ReturnsNull(string json)
    {
        Assert.Null(TokenTrackerExportParser.Parse(json, FetchedAt));
    }
}
