# Copyright 2022-2024 ETSI OSG/SDG TeraFlowSDN (TFS) (https://tfs.etsi.org/)
#
# 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.

import logging
from typing import Dict, List, Optional, Tuple, Union
from device.service.driver_api._Driver import RESOURCE_ENDPOINTS, RESOURCE_INTERFACES, RESOURCE_NETWORK_INSTANCES
from ._Handler import _Handler
from .Component import ComponentHandler
from .Interface import InterfaceHandler
from .InterfaceCounter import InterfaceCounterHandler
from .NetworkInstance import NetworkInstanceHandler
from .NetworkInstanceInterface import NetworkInstanceInterfaceHandler
from .NetworkInstanceStaticRoute import NetworkInstanceStaticRouteHandler
from .Tools import get_schema

LOGGER = logging.getLogger(__name__)

comph  = ComponentHandler()
ifaceh = InterfaceHandler()
ifctrh = InterfaceCounterHandler()
nih    = NetworkInstanceHandler()
niifh  = NetworkInstanceInterfaceHandler()
nisrh  = NetworkInstanceStaticRouteHandler()

ALL_RESOURCE_KEYS = [
    RESOURCE_ENDPOINTS,
    RESOURCE_INTERFACES,
    RESOURCE_NETWORK_INSTANCES,
]

RESOURCE_KEY_MAPPER = {
    RESOURCE_ENDPOINTS         : comph.get_resource_key(),
    RESOURCE_INTERFACES        : ifaceh.get_resource_key(),
    RESOURCE_NETWORK_INSTANCES : nih.get_resource_key(),
}

PATH_MAPPER = {
    '/components'        : comph.get_path(),
    '/interfaces'        : ifaceh.get_path(),
    '/network-instances' : nih.get_path(),
}

RESOURCE_KEY_TO_HANDLER = {
    comph.get_resource_key()  : comph,
    ifaceh.get_resource_key() : ifaceh,
    ifctrh.get_resource_key() : ifctrh,
    nih.get_resource_key()    : nih,
    niifh.get_resource_key()  : niifh,
    nisrh.get_resource_key()  : nisrh,
}

PATH_TO_HANDLER = {
    comph.get_path()  : comph,
    ifaceh.get_path() : ifaceh,
    ifctrh.get_path() : ifctrh,
    nih.get_path()    : nih,
    niifh.get_path()  : niifh,
    nisrh.get_path()  : nisrh,
}

def get_handler(
    resource_key : Optional[str] = None, path : Optional[str] = None, raise_if_not_found=True
) -> Optional[_Handler]:
    if (resource_key is None) == (path is None):
        MSG = 'Exactly one of resource_key({:s}) or path({:s}) must be specified'
        raise Exception(MSG.format(str(resource_key), str(path))) # pylint: disable=broad-exception-raised
    if resource_key is not None:
        resource_key_schema = get_schema(resource_key)
        resource_key_schema = RESOURCE_KEY_MAPPER.get(resource_key_schema, resource_key_schema)
        handler = RESOURCE_KEY_TO_HANDLER.get(resource_key_schema)
        if handler is None and raise_if_not_found:
            MSG = 'Handler not found: resource_key={:s} resource_key_schema={:s}'
            # pylint: disable=broad-exception-raised
            raise Exception(MSG.format(str(resource_key), str(resource_key_schema)))
    elif path is not None:
        path_schema = get_schema(path)
        path_schema = PATH_MAPPER.get(path_schema, path_schema)
        handler = PATH_TO_HANDLER.get(path_schema)
        if handler is None and raise_if_not_found:
            MSG = 'Handler not found: resource_key={:s} resource_key_schema={:s}'
            # pylint: disable=broad-exception-raised
            raise Exception(MSG.format(str(resource_key), str(resource_key_schema)))
    return handler

def get_path(resource_key : str) -> str:
    return get_handler(resource_key=resource_key).get_path()

def parse(str_path : str, value : Union[Dict, List]):
    return get_handler(path=str_path).parse(value)

def compose(resource_key : str, resource_value : Union[Dict, List], delete : bool = False) -> Tuple[str, str]:
    return get_handler(resource_key=resource_key).compose(resource_key, resource_value, delete=delete)
