using Google.Protobuf.WellKnownTypes;
using ZB.MOM.WW.MxGateway.Contracts.Proto;

namespace ZB.MOM.WW.MxGateway.Server.Grpc;

/// <summary>
/// Maps between worker IPC types and gRPC contract types.
/// </summary>
public sealed class MxAccessGrpcMapper
{
    private readonly TimeProvider _timeProvider;

    /// <summary>
    /// Initializes the mapper with an optional time provider.
    /// </summary>
    /// <param name="timeProvider">Time provider for timestamps; defaults to system time if null.</param>
    public MxAccessGrpcMapper(TimeProvider? timeProvider = null)
    {
        _timeProvider = timeProvider ?? TimeProvider.System;
    }

    /// <summary>
    /// Maps a gRPC MX command request to a worker command.
    /// </summary>
    /// <param name="request">Request payload.</param>
    public WorkerCommand MapCommand(MxCommandRequest request)
    {
        ArgumentNullException.ThrowIfNull(request);
        ArgumentNullException.ThrowIfNull(request.Command);

        return new WorkerCommand
        {
            Command = request.Command.Clone(),
            EnqueueTimestamp = Timestamp.FromDateTimeOffset(_timeProvider.GetUtcNow()),
        };
    }

    /// <summary>
    /// Maps a worker command reply to a gRPC MX command reply.
    /// </summary>
    /// <param name="reply">Worker command reply.</param>
    public MxCommandReply MapCommandReply(WorkerCommandReply reply)
    {
        ArgumentNullException.ThrowIfNull(reply);

        if (reply.Reply is null)
        {
            return new MxCommandReply
            {
                ProtocolStatus = ProtocolViolation("Worker command reply did not contain a public reply payload."),
            };
        }

        return reply.Reply.Clone();
    }

    /// <summary>
    /// Maps a worker event to a gRPC MX event.
    /// </summary>
    /// <param name="workerEvent">Worker event to map.</param>
    public MxEvent MapEvent(WorkerEvent workerEvent)
    {
        ArgumentNullException.ThrowIfNull(workerEvent);

        return workerEvent.Event?.Clone() ?? new MxEvent
        {
            Family = MxEventFamily.Unspecified,
            RawStatus = "Worker event did not contain a public event payload.",
        };
    }

    /// <summary>
    /// Creates an OK protocol status.
    /// </summary>
    /// <param name="message">Status message.</param>
    public static ProtocolStatus Ok(string message = "OK")
    {
        return new ProtocolStatus
        {
            Code = ProtocolStatusCode.Ok,
            Message = message,
        };
    }

    /// <summary>
    /// Creates an InvalidRequest protocol status.
    /// </summary>
    /// <param name="message">Status message.</param>
    public static ProtocolStatus InvalidRequest(string message)
    {
        return new ProtocolStatus
        {
            Code = ProtocolStatusCode.InvalidRequest,
            Message = message,
        };
    }

    /// <summary>
    /// Creates a SessionNotFound protocol status.
    /// </summary>
    /// <param name="message">Status message.</param>
    public static ProtocolStatus SessionNotFound(string message)
    {
        return new ProtocolStatus
        {
            Code = ProtocolStatusCode.SessionNotFound,
            Message = message,
        };
    }

    /// <summary>
    /// Creates a SessionNotReady protocol status.
    /// </summary>
    /// <param name="message">Status message.</param>
    public static ProtocolStatus SessionNotReady(string message)
    {
        return new ProtocolStatus
        {
            Code = ProtocolStatusCode.SessionNotReady,
            Message = message,
        };
    }

    /// <summary>
    /// Creates a WorkerUnavailable protocol status.
    /// </summary>
    /// <param name="message">Status message.</param>
    public static ProtocolStatus WorkerUnavailable(string message)
    {
        return new ProtocolStatus
        {
            Code = ProtocolStatusCode.WorkerUnavailable,
            Message = message,
        };
    }

    /// <summary>
    /// Creates a Timeout protocol status.
    /// </summary>
    /// <param name="message">Status message.</param>
    public static ProtocolStatus Timeout(string message)
    {
        return new ProtocolStatus
        {
            Code = ProtocolStatusCode.Timeout,
            Message = message,
        };
    }

    /// <summary>
    /// Creates a Canceled protocol status.
    /// </summary>
    /// <param name="message">Status message.</param>
    public static ProtocolStatus Canceled(string message)
    {
        return new ProtocolStatus
        {
            Code = ProtocolStatusCode.Canceled,
            Message = message,
        };
    }

    /// <summary>
    /// Creates a ProtocolViolation protocol status.
    /// </summary>
    /// <param name="message">Status message.</param>
    public static ProtocolStatus ProtocolViolation(string message)
    {
        return new ProtocolStatus
        {
            Code = ProtocolStatusCode.ProtocolViolation,
            Message = message,
        };
    }
}
