import grpc, logging
from prometheus_client import Counter, Histogram
from common.database.api.context.Constants import DEFAULT_CONTEXT_ID, DEFAULT_TOPOLOGY_ID
from common.database.api.Database import Database
from common.exceptions.ServiceException import ServiceException
from slice.proto.context_pb2 import Empty
from slice.proto.slice_pb2 import SliceStatus, TransportSlice
from slice.proto.slice_pb2_grpc import SliceServiceServicer

LOGGER = logging.getLogger(__name__)

CREATEUPDATESLICE_COUNTER_STARTED    = Counter  ('slice_createupdateslice_counter_started',
                                                 'Slice:CreateUpdateSlice counter of requests started'  )
CREATEUPDATESLICE_COUNTER_COMPLETED  = Counter  ('slice_createupdateslice_counter_completed',
                                                 'Slice:CreateUpdateSlice counter of requests completed')
CREATEUPDATESLICE_COUNTER_FAILED     = Counter  ('slice_createupdateslice_counter_failed',
                                                 'Slice:CreateUpdateSlice counter of requests failed'   )
CREATEUPDATESLICE_HISTOGRAM_DURATION = Histogram('slice_createupdateslice_histogram_duration',
                                                 'Slice:CreateUpdateSlice histogram of request duration')

DELETESLICE_COUNTER_STARTED    = Counter  ('slice_DeleteSlice_counter_started',
                                           'Slice:DeleteSlice counter of requests started'  )
DELETESLICE_COUNTER_COMPLETED  = Counter  ('slice_DeleteSlice_counter_completed',
                                           'Slice:DeleteSlice counter of requests completed')
DELETESLICE_COUNTER_FAILED     = Counter  ('slice_DeleteSlice_counter_failed',
                                           'Slice:DeleteSlice counter of requests failed'   )
DELETESLICE_HISTOGRAM_DURATION = Histogram('slice_DeleteSlice_histogram_duration',
                                           'Slice:DeleteSlice histogram of request duration')

class SliceServiceServicerImpl(SliceServiceServicer):
    def __init__(self, database : Database):
        LOGGER.debug('Creating Servicer...')
        self.database = database
        LOGGER.debug('Servicer Created')

    @CREATEUPDATESLICE_HISTOGRAM_DURATION.time()
    def CreateUpdateSlice(self, request : TransportSlice, grpc_context : grpc.ServicerContext) -> SliceStatus:
        CREATEUPDATESLICE_COUNTER_STARTED.inc()
        try:
            LOGGER.debug('CreateUpdateSlice request: {}'.format(str(request)))

            # ----- Validate request data and pre-conditions -----------------------------------------------------------
            device_id, device_type, device_config, device_opstat, db_endpoints_ports = \
                check_slice_request('CreateUpdateSlice', request, self.database, LOGGER)

            # ----- Implement changes in the database ------------------------------------------------------------------
            db_context = self.database.context(DEFAULT_CONTEXT_ID).create()
            db_topology = db_context.topology(DEFAULT_TOPOLOGY_ID).create()
            db_device = db_topology.device(device_id).create(device_type, device_config, device_opstat)
            for db_endpoint,port_type in db_endpoints_ports:
                db_endpoint.create(port_type)

            # ----- Compose reply --------------------------------------------------------------------------------------
            reply = SliceStatus(**db_device.dump_id())
            LOGGER.debug('CreateUpdateSlice reply: {}'.format(str(reply)))
            CREATEUPDATESLICE_COUNTER_COMPLETED.inc()
            return reply
        except ServiceException as e:
            LOGGER.exception('CreateUpdateSlice exception')
            CREATEUPDATESLICE_COUNTER_FAILED.inc()
            grpc_context.abort(e.code, e.details)
        except Exception as e:                                      # pragma: no cover
            LOGGER.exception('CreateUpdateSlice exception')
            CREATEUPDATESLICE_COUNTER_FAILED.inc()
            grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))

    @DELETESLICE_HISTOGRAM_DURATION.time()
    def DeleteSlice(self, request : TransportSlice, grpc_context : grpc.ServicerContext) -> Empty:
        DELETESLICE_COUNTER_STARTED.inc()
        try:
            LOGGER.debug('DeleteSlice request: {}'.format(str(request)))

            # ----- Validate request data and pre-conditions -----------------------------------------------------------
            device_id = check_slice_id_request('DeleteSlice', request, self.database, LOGGER)

            # ----- Implement changes in the database ------------------------------------------------------------------
            db_context = self.database.context(DEFAULT_CONTEXT_ID).create()
            db_topology = db_context.topology(DEFAULT_TOPOLOGY_ID).create()
            db_topology.device(device_id).delete()

            # ----- Compose reply --------------------------------------------------------------------------------------
            reply = Empty()
            LOGGER.debug('DeleteSlice reply: {}'.format(str(reply)))
            DELETESLICE_COUNTER_COMPLETED.inc()
            return reply
        except ServiceException as e:
            LOGGER.exception('DeleteSlice exception')
            DELETESLICE_COUNTER_FAILED.inc()
            grpc_context.abort(e.code, e.details)
        except Exception as e:                                      # pragma: no cover
            LOGGER.exception('DeleteSlice exception')
            DELETESLICE_COUNTER_FAILED.inc()
            grpc_context.abort(grpc.StatusCode.INTERNAL, str(e))
