package com.mobify.astro.messaging;

import org.json.JSONException;
import org.json.JSONObject;

import java.lang.reflect.Method;

public class Exceptions {
    private Exceptions() {}

    public static class RpcException extends Exception {
        public RpcException(String exceptionMessage) {
            super(exceptionMessage);
        }
    }

    public static class HeaderSerializationException extends Exception {
        public HeaderSerializationException(Message message, JSONException cause) {
            super("Failed to set headers for message with hash code: " +
                    Integer.toString(message.hashCode()));
            initCause(cause);
        }
    }

    public static class EventCreationException extends Exception {
        public EventCreationException(String eventName, JSONException cause) {
            super("Failed to create event: " + eventName);
            initCause(cause);
        }
    }

    public static class InvalidEventMethodException extends Exception {
        public InvalidEventMethodException(String methodName, String message) {
            super("Method '" + methodName + "' is not an valid event method. " + message);
        }
    }

    public static class SerializationException extends RuntimeException {
        public SerializationException(String message) {
            super(message);
        }
        public SerializationException(Message message, Exception cause) {
            super("Failed to serialize Message with hash code " + Integer.toString(message.hashCode()));
            initCause(cause);
        }
    }

    public static class PayloadSerializationException extends SerializationException {
        public PayloadSerializationException(Message message, JSONObject payload, JSONException cause) {
            super("Failed to set payload " + payload.toString() + " on message with hash code" +
                    Integer.toString(message.hashCode()));
            initCause(cause);
        }
    }

    public static class PayloadMissingException extends Exception {
        public PayloadMissingException(JSONObject serializedMessage) {
            super("Failed to find payload key in message " + serializedMessage.toString());
        }
    }

    public static class UnannotatedMethodException extends RpcException {
        public UnannotatedMethodException(Method method) {
            super("Expected method " + method.getName() + " of class " +
                    method.getDeclaringClass().getCanonicalName() + " to be annotated " +
                    "with @RpcMethod or @AsyncRpcMethod");
        }
    }

    public static class MissingRpcMessageArgumentException extends RpcException {
        public MissingRpcMessageArgumentException(Method method) {
            super("Expected method " + method.getName() +
                " of class " + method.getDeclaringClass().getCanonicalName() +
                " to take an RpcResponse as the first parameter.");
        }
    }

    public static class AnnotationArityMismatch extends RpcException {
        public AnnotationArityMismatch(Method method, int arity) {
            super("Method " + method.getName() + " of Class " +
                    method.getDeclaringClass().getCanonicalName() + " expects " +
                    method.getParameterTypes().length + " parameters. Annotated to take " + arity);
        }
    }

    public static class MalformedRpcRequestMessage extends RpcException {
        public MalformedRpcRequestMessage(String missingKeyName, Message message) {
            super("RpcRequest message missing key \"" + missingKeyName + "\": " +
                    message.toString());
        }
    }

    public static class MalformedRpcMessageException extends RpcException {
        public MalformedRpcMessageException(JSONObject serializedMessage) {
            super("Failed to create RpcRequest or RpcResponse from message with ID header: " +
                    serializedMessage.toString());
        }
    }

    public static class MethodNotFoundException extends RpcException {
        public MethodNotFoundException(String methodName, MessageListener ml) {
            super("Method named " + methodName + " not found on instance of " +
                    ml.getClass().getCanonicalName());
        }
    }

    public static class ParametersNotFoundException extends RpcException {
        public ParametersNotFoundException(Message message) {
            super("Error unpacking RPC parameters from message: " + message.toString());
        }
    }

    public static class ParameterMissingException extends RpcException {
        public ParameterMissingException(String parameterName, Message message) {
            super("Named parameter \"" + parameterName + "\" missing in message: " + message.toString());
        }
    }

    public static class ResponseCreationException extends RpcException {
        public ResponseCreationException(RpcRequest request, Exception e) {
            super("Error creating response to RpcRequest: " + request.toString());
            this.initCause(e);
        }
    }

    public static class ResultPackingException extends RpcException {
        public ResultPackingException(Object returnValue, Method method, Object[] parameters) {
            super("Error packing return value \"" + returnValue.toString() +
                    "\" of method " + method.getName() + " of class " +
                    method.getDeclaringClass().getCanonicalName() + " with parameters " +
                    parameters);
        }
    }

    public static class DuplicateRpcResponseSend extends RuntimeException {
        public DuplicateRpcResponseSend(RpcMessage message) {
            super("DuplicateRpcResponseSend: Tried to respond more than once with response message "
                    + message);
        }
    }
}
