# -*- coding: utf-8 -*-

# Copyright 2017-2018 ICON Foundation
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from jsonschema import validate
from jsonschema.exceptions import ValidationError


icx_getBlockByHeight: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_getBlockByHeight",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_getblockbyheight",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
        "params": {
            "type": "object",
            "properties": {
                "height": {"type": "string"},
            },
            "additionalProperties": False,
            "required": ["height"]
        }
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id", "params"]
}

icx_getBlockByHash: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_getBlockByHash",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_getblockbyhash",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
        "params": {
            "type": "object",
            "properties": {
                "hash": {"type": "string"},
            },
            "additionalProperties": False,
            "required": ["hash"]
        }
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id", "params"]
}

icx_getLastBlock: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_getLastBlock",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_getlastblock",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id"]
}

icx_getTotalSupply: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_getTotalSupply",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_gettotalsupply",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id"]
}

icx_call_v3: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_call",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_call",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string", "enum": ["icx_call"]},
        "id": {"type": "number"},
        "params": {
            "type": "object",
            "properties": {
                "from": {"type": "string", "maxLength": 42},
                "to": {"type": "string", "maxLength": 42},
                "dataType": {"type": "string", "enum": ["call"]},
                "data": {
                    "type": "object",
                    "properties": {
                        "method": {"type": "string"},
                        "params": {"type": "object"}
                    },
                    "additionalProperties": False,
                    "required": ["method"]
                },
            },
            "additionalProperties": False,
            "required": ["from", "to", "dataType", "data"]
        }
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id", "params"]
}

icx_getBalance_v3: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_getBalance",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_getbalance",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
        "params": {
            "type": "object",
            "properties": {
                "address": {"type": "string", "maxLength": 42, "pattern": "^hx|^cx"},
            },
            "additionalProperties": False,
            "required": ["address"]
        }
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id", "params"]
}

icx_getScoreApi_v3: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_getScoreApi",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_getscoreapi",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
        "params": {
            "type": "object",
            "properties": {
                "address": {"type": "string", "maxLength": 42, "pattern": "^cx"},
            },
            "additionalProperties": False,
            "required": ["address"]
        }
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id", "params"]
}

icx_getTransactionResult_v3: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_getTransactionResult",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_gettransactionresult",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
        "params": {
            "type": "object",
            "properties": {
                "txHash": {"type": "string"}
            },
            "additionalProperties": False,
            "required": ["txHash"]
        }
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id", "params"]
}

icx_getTransactionByHash_v3: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_getTransactionByHash",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_gettransactionbyhash",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
        "params": {
            "type": "object",
            "properties": {
                "txHash": {"type": "string", "minLength": 66, "maxLength": 66, "pattern": "^0x[0-9a-f]{64}"}
            },
            "additionalProperties": False,
            "required": ["txHash"]
        }
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id", "params"]
}

icx_sendTransaction_v3: dict = {
    "$schema": "http://json-schema.org/schema#",
    "title": "icx_sendTransaction",
    "id": "https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md#icx_sendtransaction",
    "type": "object",
    "properties": {
        "jsonrpc": {"type": "string", "enum": ["2.0"]},
        "method": {"type": "string"},
        "id": {"type": "number"},
        "params": {
            "type": "object",
            "properties": {
                "version": {"type": "string"},
                "from": {"type": "string", "maxLength": 42},
                "to": {"type": "string", "maxLength": 42},
                "value": {"type": "string"},
                "stepLimit": {"type": "string"},
                "timestamp": {"type": "string"},
                "nid": {"type": "string"},
                "nonce": {"type": "string"},
                "signature": {"type": "string"},
                "dataType": {"type": "string", "enum": ["call", "deploy", "message"]},
                "data": {
                    "oneOf": [
                        {
                            "type": "object",
                            "properties": {
                                "method": {"type": "string"},
                                "contentType": {"type": "string"},
                                "content": {"type": "string"},
                                "params": {"type": "object"}
                            },
                            "additionalProperties": False,
                        },
                        {"type": "string"}
                    ],
                },
            },
            "additionalProperties": False,
            "required": ["version", "from", "stepLimit", "timestamp", "nid", "signature"]
        }
    },
    "additionalProperties": False,
    "required": ["jsonrpc", "method", "id", "params"]

}

SCHEMA_V3: dict = {
    "icx_getLastBlock": icx_getLastBlock,
    "icx_getBlockByHeight": icx_getBlockByHeight,
    "icx_getBlockByHash": icx_getBlockByHash,
    "icx_call": icx_call_v3,
    "icx_getBalance": icx_getBalance_v3,
    "icx_getScoreApi": icx_getScoreApi_v3,
    "icx_getTotalSupply": icx_getTotalSupply,
    "icx_getTransactionResult": icx_getTransactionResult_v3,
    "icx_getTransactionByHash": icx_getTransactionByHash_v3,
    "icx_sendTransaction": icx_sendTransaction_v3
}


def validate_jsonschema_v3(request: object):
    validate_jsonschema(request, SCHEMA_V3)


def validate_jsonschema(request: object, schemas: dict = SCHEMA_V3):
    """ Validate JSON-RPC v3 schema.

    refer to
    v3 : https://github.com/icon-project/icon-rpc-server/blob/master/docs/icon-json-rpc-v3.md

    :param request: JSON-RPC request to validate
    :param schemas: The schema to validate with
    :return: N/A
    """
    # get JSON-RPC batch request
    if isinstance(request, list):
        for req in request:
            validate_jsonschema(req, schemas=schemas)
        return

    # get schema for 'method'
    schema: dict = None
    method = request.get('method', None)
    if method and isinstance(method, str):
        schema = schemas.get(method, None)
    if schema is None:
        raise ValueError(f"JSON schema validation error: Method not found")

    # check request
    try:
        validate(instance=request, schema=schema)
    except ValidationError as e:
        raise ValueError(f"JSON schema validation error: {e}")
