import grpc, logging
from typing import Dict, List, Set, Tuple
from common.Checkers import chk_string
from common.Constants import DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID
from common.exceptions.ServiceException import ServiceException
from context.proto.context_pb2 import Link, LinkId
from context.service.database.api.Database import Database
from context.service.database.api.context.topology.device.Endpoint import Endpoint
from context.service.database.checkers.EndpointIdCheckers import check_endpoint_id
from context.service.database.checkers.DeviceCheckers import check_device_endpoint_exists
from context.service.database.checkers.LinkCheckers import check_link_exists, check_link_not_exists

def _check_link_exists(method_name : str, database : Database, link_id : str):
    if method_name in ['AddLink']:
        check_link_not_exists(database, DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, link_id)
    elif method_name in ['DeleteLink']:
        check_link_exists(database, DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, link_id)
    else:                                       # pragma: no cover (test requires malforming the code)
        msg = 'Unexpected condition [_check_link_exists(method_name={}, link_id={})]'
        msg = msg.format(str(method_name), str(link_id))
        raise ServiceException(grpc.StatusCode.UNIMPLEMENTED, msg)

def check_link_request(
    method_name : str, request : Link, database : Database, logger : logging.Logger
    ) -> Tuple[str, List[Endpoint]]:

    # ----- Parse attributes -------------------------------------------------------------------------------------------
    try:
        link_id = chk_string('link.link_id.link_id.uuid',
                             request.link_id.link_id.uuid,
                             allow_empty=False)
    except Exception as e:
        logger.exception('Invalid arguments:')
        raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e))

    # ----- Check if link exists in database ---------------------------------------------------------------------------
    _check_link_exists(method_name, database, link_id)

    # ----- Parse endpoints and check if they exist in the database as device endpoints --------------------------------
    add_topology_devices_endpoints : Dict[str, Dict[str, Set[str]]] = {}
    db_endpoints : List[Endpoint] = []
    for endpoint_number,endpoint_id in enumerate(request.endpointList):
        parent_name = 'Endpoint(#{}) of Context({})/Topology({})/Link({})'
        parent_name = parent_name.format(endpoint_number, DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, link_id)

        _, ep_device_id, ep_port_id = check_endpoint_id(
            logger, endpoint_number, parent_name, endpoint_id, add_topology_devices_endpoints)

        db_endpoint = check_device_endpoint_exists(
            database, parent_name, DEFAULT_CONTEXT_UUID, DEFAULT_TOPOLOGY_UUID, ep_device_id, ep_port_id)
        db_endpoints.append(db_endpoint)

    return link_id, db_endpoints

def check_link_id_request(
    method_name : str, request : LinkId, database : Database, logger : logging.Logger) -> str:

    # ----- Parse attributes -------------------------------------------------------------------------------------------
    try:
        link_id = chk_string('link_id.link_id.uuid',
                             request.link_id.uuid,
                             allow_empty=False)
    except Exception as e:
        logger.exception('Invalid arguments:')
        raise ServiceException(grpc.StatusCode.INVALID_ARGUMENT, str(e))

    # ----- Check if link exists in database ---------------------------------------------------------------------------
    _check_link_exists(method_name, database, link_id)

    return link_id
