using Nexus.Api.Data;
using Nexus.Api.Models;
using Nexus.Api.Services;
using Xunit;

namespace Nexus.Api.Tests;

public sealed class OpenClawChatServiceTests
{
    [Fact]
    public async Task Send_uses_durable_protocol_v4_run_and_canonical_agent_session()
    {
        var runId = Guid.NewGuid();
        var runs = new CapturingRunService(runId, ok: true);
        var service = new OpenClawChatService(runs);
        var invocation = new OpenClawInvocationMetadata(
            "idem-chat",
            "corr-chat",
            "owner",
            "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01");

        var result = await service.SendAsync(
            "coordinate this work",
            "browser-conversation",
            "Iris",
            invocation);

        Assert.NotNull(runs.Request);
        Assert.Equal("iris", runs.Request.AgentId);
        Assert.Equal("agent:iris:main", runs.Request.SessionKey);
        Assert.Equal("coordinate this work", runs.Request.Prompt);
        Assert.Equal(runId, result.RunId);
        Assert.Equal("OpenClaw Protocol v4", result.Runtime);
    }

    [Fact]
    public async Task Send_does_not_claim_success_when_gateway_dispatch_is_blocked()
    {
        var runs = new CapturingRunService(Guid.NewGuid(), ok: false);
        var service = new OpenClawChatService(runs);

        var exception = await Assert.ThrowsAsync<OpenClawChatDispatchException>(
            () => service.SendAsync(
                "coordinate this work",
                "browser-conversation",
                "iris",
                new OpenClawInvocationMetadata(
                    "idem-chat",
                    "corr-chat",
                    "owner",
                    "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01")));

        Assert.Equal("blocked", exception.State);
    }

    private sealed class CapturingRunService(Guid runId, bool ok) :
        IOpenClawRunService
    {
        public StartOpenClawRunRequest? Request { get; private set; }

        public Task<OpenClawRunOperationDto> StartAsync(
            StartOpenClawRunRequest request,
            OpenClawInvocationMetadata invocation,
            CancellationToken cancellationToken = default)
        {
            Request = request;
            var run = new OpenClawRunDto(
                runId,
                "Chat",
                request.Prompt,
                request.AgentId,
                request.SessionKey,
                ok ? "running" : "blocked",
                request.TaskId,
                request.ProjectId,
                null,
                null,
                invocation.CorrelationId,
                invocation.Actor,
                ok ? null : "Gateway disconnected",
                null,
                false,
                ok,
                !ok,
                false,
                null,
                DateTimeOffset.UtcNow,
                DateTimeOffset.UtcNow,
                null,
                null);
            return Task.FromResult(new OpenClawRunOperationDto(
                ok,
                ok ? "running" : "blocked",
                ok ? "OpenClaw accepted the run." : "Gateway disconnected.",
                run,
                null,
                DateTimeOffset.UtcNow));
        }

        public Task<OpenClawRunCollectionDto> GetAsync(
            OpenClawRunQuery query,
            CancellationToken cancellationToken = default) =>
            throw new NotSupportedException();

        public Task<OpenClawRunDto?> GetByIdAsync(
            Guid id,
            CancellationToken cancellationToken = default) =>
            throw new NotSupportedException();

        public Task<OpenClawRunOperationDto?> StopAsync(
            Guid id,
            string? reason,
            OpenClawInvocationMetadata invocation,
            CancellationToken cancellationToken = default) =>
            throw new NotSupportedException();

        public Task<OpenClawRunOperationDto?> ResumeAsync(
            Guid id,
            string? reason,
            OpenClawInvocationMetadata invocation,
            CancellationToken cancellationToken = default) =>
            throw new NotSupportedException();

        public Task<OpenClawRunOperationDto?> RetryAsync(
            Guid id,
            string? reason,
            OpenClawInvocationMetadata invocation,
            CancellationToken cancellationToken = default) =>
            throw new NotSupportedException();

        public Task<OpenClawRunHistoryResponse?> GetHistoryAsync(
            Guid id,
            int gatewayLimit = 200,
            CancellationToken cancellationToken = default) =>
            throw new NotSupportedException();

        public Task ReconcileAsync(
            GatewayEventEnvelope gatewayEvent,
            CancellationToken cancellationToken = default) =>
            throw new NotSupportedException();
    }
}
