using ZB.MOM.WW.MxGateway.Contracts.Proto;
using ZB.MOM.WW.MxGateway.Server.Grpc;

namespace ZB.MOM.WW.MxGateway.Tests.Gateway.Grpc;

public sealed class MxAccessGrpcMapperTests
{
    /// <summary>Verifies that command mapping clones payloads to isolate them across process boundaries.</summary>
    [Fact]
    public void MapCommand_ClonesMethodSpecificPayloadForWorkerBoundary()
    {
        MxAccessGrpcMapper mapper = new();
        MxCommandRequest request = new()
        {
            SessionId = "session-1",
            Command = new MxCommand
            {
                Kind = MxCommandKind.Write,
                Write = new WriteCommand
                {
                    ServerHandle = 10,
                    ItemHandle = 20,
                    UserId = 30,
                    Value = new MxValue
                    {
                        DataType = MxDataType.String,
                        StringValue = "value",
                    },
                },
            },
        };

        WorkerCommand workerCommand = mapper.MapCommand(request);
        request.Command.Write.Value.StringValue = "changed";

        Assert.Equal(MxCommandKind.Write, workerCommand.Command.Kind);
        Assert.Equal("value", workerCommand.Command.Write.Value.StringValue);
        Assert.NotNull(workerCommand.EnqueueTimestamp);
    }

    /// <summary>Verifies that command reply mapping preserves HRESULT and status information.</summary>
    [Fact]
    public void MapCommandReply_PreservesHresultStatusesAndPayload()
    {
        const int hresult = unchecked((int)0x80070005);
        WorkerCommandReply workerReply = new()
        {
            Reply = new MxCommandReply
            {
                SessionId = "session-1",
                Kind = MxCommandKind.Register,
                ProtocolStatus = MxAccessGrpcMapper.Ok(),
                Hresult = hresult,
                Register = new RegisterReply { ServerHandle = 50 },
            },
        };
        workerReply.Reply.Statuses.Add(new MxStatusProxy
        {
            Success = 0,
            Category = MxStatusCategory.SecurityError,
            DiagnosticText = "denied",
        });

        MxCommandReply publicReply = new MxAccessGrpcMapper().MapCommandReply(workerReply);

        Assert.Equal(hresult, publicReply.Hresult);
        Assert.Equal(50, publicReply.Register.ServerHandle);
        Assert.Equal("denied", Assert.Single(publicReply.Statuses).DiagnosticText);
    }

    /// <summary>Verifies that a missing worker reply returns a protocol violation status.</summary>
    [Fact]
    public void MapCommandReply_WhenWorkerReplyMissing_ReturnsProtocolViolationReply()
    {
        MxCommandReply publicReply = new MxAccessGrpcMapper().MapCommandReply(new WorkerCommandReply());

        Assert.Equal(ProtocolStatusCode.ProtocolViolation, publicReply.ProtocolStatus.Code);
    }
}
