package com.zb.mom.ww.mxgateway.client;

import io.grpc.Status;
import io.grpc.StatusRuntimeException;
import mxaccess_gateway.v1.MxaccessGateway.MxCommandReply;
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatus;
import mxaccess_gateway.v1.MxaccessGateway.ProtocolStatusCode;

final class MxGatewayErrors {
    private MxGatewayErrors() {
    }

    static RuntimeException fromGrpc(String operation, RuntimeException error) {
        if (error instanceof StatusRuntimeException statusError) {
            Status status = statusError.getStatus();
            String message = MxGatewaySecrets.redactCredentials(status.getDescription());
            return switch (status.getCode()) {
                case UNAUTHENTICATED -> new MxGatewayAuthenticationException(
                        "authentication failed: " + message, statusError);
                case PERMISSION_DENIED -> new MxGatewayAuthorizationException(
                        "authorization failed: " + message, statusError);
                case DEADLINE_EXCEEDED -> new MxGatewayException("gateway call timed out: " + message, statusError);
                case CANCELLED -> new MxGatewayException("gateway call cancelled: " + message, statusError);
                default -> new MxGatewayException("gateway " + operation + " failed: " + message, statusError);
            };
        }

        return new MxGatewayException("gateway " + operation + " failed: " + error.getMessage(), error);
    }

    static void ensureProtocolSuccess(String operation, ProtocolStatus status, MxCommandReply reply) {
        if (status == null || status.getCode() == ProtocolStatusCode.PROTOCOL_STATUS_CODE_OK) {
            return;
        }

        throw switch (status.getCode()) {
            case PROTOCOL_STATUS_CODE_SESSION_NOT_FOUND, PROTOCOL_STATUS_CODE_SESSION_NOT_READY ->
                    new MxGatewaySessionException(operation, status);
            case PROTOCOL_STATUS_CODE_WORKER_UNAVAILABLE, PROTOCOL_STATUS_CODE_PROTOCOL_VIOLATION ->
                    new MxGatewayWorkerException(operation, status);
            case PROTOCOL_STATUS_CODE_MXACCESS_FAILURE -> new MxAccessException(operation, status, reply);
            default -> new MxGatewayCommandException(operation, status, reply);
        };
    }

    static void ensureMxAccessSuccess(String operation, MxCommandReply reply) {
        if (reply == null) {
            return;
        }
        // COM semantics: only a negative HRESULT is a failure. Positive success
        // codes such as S_FALSE (1) pass.
        if (reply.hasHresult() && reply.getHresult() < 0) {
            throw new MxAccessException(operation, reply);
        }
        for (var status : reply.getStatusesList()) {
            if (!MxStatuses.succeeded(status)) {
                throw new MxAccessException(operation, reply);
            }
        }
    }

    static String protocolStatusMessage(String operation, ProtocolStatus status) {
        if (status == null) {
            return "mxgateway " + operation + " failed with missing protocol status";
        }
        if (status.getMessage().isBlank()) {
            return "mxgateway " + operation + " failed with protocol status " + status.getCode();
        }
        return "mxgateway " + operation + " failed with protocol status "
                + status.getCode()
                + ": "
                + status.getMessage();
    }
}
