using System.Collections.Concurrent;
using System.Text.Json;
using ClawdDotNet.Core.Tools;

namespace ClawdDotNet.Core.Tests.Infrastructure;

/// <summary>
/// Konfigurierbares Tool für Engine-Tests: liefert feste Ergebnisse, kann verzögern
/// (um Nebenläufigkeit zu provozieren) oder werfen.
/// </summary>
internal sealed class FakeTool : IAgentTool
{
    private readonly Func<JsonElement, AgentToolContext, CancellationToken, Task<ToolResult>> _handler;

    public string Name { get; }
    public string Description => $"Test-Tool {Name}";

    public JsonElement InputSchema { get; } = JsonDocument.Parse("""
        { "type": "object", "properties": { "action": { "type": "string" } } }
        """).RootElement.Clone();

    /// <summary>Jeder Aufruf wird mitgeschrieben — inklusive der aufrufenden Agent-Id.</summary>
    public ConcurrentBag<string> Invocations { get; } = new();

    private FakeTool(string name, Func<JsonElement, AgentToolContext, CancellationToken, Task<ToolResult>> handler)
    {
        Name = name;
        _handler = handler;
    }

    public static FakeTool Returning(string result, string name = "TestTool")
        => new(name, (_, _, _) => Task.FromResult(ToolResult.Ok(result)));

    /// <summary>Verzögert die Ausführung — vergrößert das Zeitfenster für Races.</summary>
    public static FakeTool Slow(TimeSpan delay, string result = "ok", string name = "TestTool")
        => new(name, async (_, _, ct) =>
        {
            await Task.Delay(delay, ct);
            return ToolResult.Ok(result);
        });

    public static FakeTool Throwing(Exception ex, string name = "TestTool")
        => new(name, (_, _, _) => throw ex);

    public Task<ToolResult> ExecuteAsync(JsonElement input, AgentToolContext context, CancellationToken ct)
    {
        Invocations.Add(context.AgentId);
        return _handler(input, context, ct);
    }
}

internal sealed class InMemoryStateStore : ClawdDotNet.Core.State.IStateStore
{
    private readonly ConcurrentDictionary<string, string> _values = new();

    public Task<string?> GetAsync(string key, CancellationToken ct)
        => Task.FromResult(_values.GetValueOrDefault(key));

    public Task SetAsync(string key, string value, CancellationToken ct)
    {
        _values[key] = value;
        return Task.CompletedTask;
    }

    public Task DeleteAsync(string key, CancellationToken ct)
    {
        _values.TryRemove(key, out _);
        return Task.CompletedTask;
    }
}
