import json
import jsonschema
import pytest

DEFAULT_REQUEST_HEADERS = {
    "Content-type": "application/json",
    "Accept": ["application/json"]
}

JUNIPER_LINK_METADATA = {
    "$schema": "http://json-schema.org/draft-07/schema#",
    "type": "object",

    "definitions": {
        "ip-address": {
            "type": "string",
            "oneOf": [
                {"pattern": r'^(\d+\.){3}\d+$'},
                {"pattern": r'^([a-f\d]{4}:){7}[a-f\d]{4}$'}
            ]
        },
        "ipv4-interface-address": {
            "type": "string",
            "pattern": r'^(\d+\.){3}\d+/\d+$'
        },
        "ipv6-interface-address": {
            "type": "string",
            "pattern": r'^[a-f\d:]+/\d+$'
        },
        "interface-info": {
            "type": "object",
            "properties": {
                "name": {"type": "string"},
                "description": {"type": "string"},
                "ipv4": {"$ref": "#/definitions/ipv4-interface-addres"},
                "ipv4": {"$ref": "#/definitions/ipv6-interface-addres"},
            },
            "required": ["name", "description", "ipv4", "ipv6"],
            "additionalProperties": False
        },
        "service-info": {
            "type": "object"
        }
    },

    "type": "object",
    "properties": {
        "services": {
            "type": "array",
            "items": {"$ref": "#/definitions/service-info"}
        },
        "interface": {"$ref": "#/definitions/interface-info"},
        "related-services": {
            "type": "array",
            "items": {"$ref": "#/definitions/service-info"}
        }
    },
    "additionalProperties": False
}


def test_trap_metadata(client_with_mocked_data):
    response_schema = {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object"
    }
    rv = client_with_mocked_data.get(
        '/classifier/trap-metadata/mx1.ams.nl.geant.net/ae15.1500',
        headers=DEFAULT_REQUEST_HEADERS)
    assert rv.status_code == 200
    assert rv.is_json
    response_data = json.loads(rv.data.decode('utf-8'))
    jsonschema.validate(response_data, response_schema)


VPN_RR_PEER_INFO_KEYS = {'vpn-rr-peer-info'}
IX_PUBLIC_PEER_INFO_KEYS = {'ix-public-peer-info', 'interfaces'}


@pytest.mark.parametrize('peer_address,expected_response_keys', [
    ('109.105.110.54', VPN_RR_PEER_INFO_KEYS),
    ('2001:07f8:001c:024a:0000:0000:316e:0001', IX_PUBLIC_PEER_INFO_KEYS),
    ('2001:07f8:000b:0100:01d1:a5d1:0310:0029', IX_PUBLIC_PEER_INFO_KEYS),
    ('195.66.224.238', IX_PUBLIC_PEER_INFO_KEYS),
]
)
def test_peer_info(
        client_with_mocked_data, peer_address, expected_response_keys):
    response_schema = {
        "$schema": "http://json-schema.org/draft-07/schema#",
        "type": "object",

        "definitions": {
            "ip-address": {
                "type": "string",
                "oneOf": [
                    {"pattern": r'^(\d+\.){3}\d+$'},
                    {"pattern": r'^([a-f\d]{4}:){7}[a-f\d]{4}$'}
                ]
            },
            "interface-address": {
                "type": "string",
                "oneOf": [
                    {"pattern": r'^(\d+\.){3}\d+/\d+$'},
                    {"pattern": r'^[a-f\d:]+/\d+$'}
                ]
            },
            "vpn-rr-peer": {
                "type": "object",
                "properties": {
                    "name": {"$ref": "#/definitions/ip-address"},
                    "description": {"type": "string"},
                    "peer-as": {"type": "integer"},
                    "router": {"type": "string"}
                },
                "required": ["name", "description"],
                "additionalProperties": False
            },
            "ix-public-peer": {
                "type": "object",
                "properties": {
                    "name": {"$ref": "#/definitions/ip-address"},
                    "description": {"type": "string"},
                    "router": {"type": "string"},
                    "as": {
                        "type": "object",
                        "properties": {
                            "local": {"type": "integer"},
                            "peer": {"type": "integer"},
                        },
                        "required": ["local", "peer"],
                        "additionalProperties": False
                    }
                },
                "required": ["name", "description", "as"],
                "additionalProperties": False
            },
            "ix-public-peer-list": {
                "type": "array",
                "items": {"$ref": "#/definitions/ip-address"}
            },
            "ix-public-peer-info": {
                "type": "object",
                "properties": {
                    "peer": {"$ref": "#/definitions/ix-public-peer"},
                    "group": {"$ref": "#/definitions/ix-public-peer-list"},
                    "router": {"$ref": "#/definitions/ix-public-peer-list"}
                },
                "required": ["peer", "group", "router"],
                "additionalProperties": False
            },
            "interface-info": {
                "type": "object",
                "properties": {
                    "name": {"$ref": "#/definitions/ip-address"},
                    "interface address": {
                        "$ref": "#/definitions/interface-address"},
                    "interface name": {"type": "string"},
                    "router": {"type": "string"}
                },
                "required": [
                    "name", "interface address", "interface name", "router"],
                "additionalProperties": False
            },
            "service-info": {
                "type": "object"
            },
            "interface-lookup-info": {
                "type": "object",
                "properties": {
                    "interface": {"$ref": "#/definitions/interface-info"},
                    "services": {
                        "type": "array",
                        "items": {"$ref": "#/definitions/service-info"}
                    }
                }
            }
        },

        "type": "object",
        "properties": {
            "ix-public-peer-info": {
                "$ref": "#/definitions/ix-public-peer-info"},
            "vpn-rr-peer-info": {"$ref": "#/definitions/vpn-rr-peer"},
            "interfaces": {
                "type": "array",
                "items": {"$ref": "#/definitions/interface-lookup-info"}
            }
        },
        "additionalProperties": False
    }

    rv = client_with_mocked_data.get(
        '/classifier/peer-info/%s' % peer_address,
        headers=DEFAULT_REQUEST_HEADERS)
    assert rv.status_code == 200
    assert rv.is_json
    response_data = json.loads(rv.data.decode('utf-8'))
    jsonschema.validate(response_data, response_schema)

    assert set(response_data.keys()) == expected_response_keys


def test_peer_invalid_address(client_with_mocked_data):
    rv = client_with_mocked_data.get(
        '/classifier/peer-info/1.2.3.4.5',
        headers=DEFAULT_REQUEST_HEADERS)
    assert rv.status_code == 422


def test_peer_not_found(client_with_mocked_data):
    rv = client_with_mocked_data.get(
        '/classifier/peer-info/1.2.3.4',
        headers=DEFAULT_REQUEST_HEADERS)
    assert rv.status_code == 404