# Copyright 2021-2023 H2020 TeraFlow (https://www.teraflow-h2020.eu/)
#
# 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 functools, logging, operator
from sqlalchemy import Column, Enum, ForeignKey
from typing import Dict, List
from common.orm.HighLevel import get_related_objects
from common.proto.context_pb2 import ServiceStatusEnum, ServiceTypeEnum
from .ConfigModel import ConfigModel
from .ConstraintModel import ConstraintsModel
from .ContextModel import ContextModel
from .Tools import grpc_to_enum
from sqlalchemy.dialects.postgresql import UUID
from context.service.database.Base import Base
import enum
LOGGER = logging.getLogger(__name__)

class ORM_ServiceTypeEnum(enum.Enum):
    UNKNOWN                   = ServiceTypeEnum.SERVICETYPE_UNKNOWN
    L3NM                      = ServiceTypeEnum.SERVICETYPE_L3NM
    L2NM                      = ServiceTypeEnum.SERVICETYPE_L2NM
    TAPI_CONNECTIVITY_SERVICE = ServiceTypeEnum.SERVICETYPE_TAPI_CONNECTIVITY_SERVICE

grpc_to_enum__service_type = functools.partial(
    grpc_to_enum, ServiceTypeEnum, ORM_ServiceTypeEnum)

class ORM_ServiceStatusEnum(enum.Enum):
    UNDEFINED       = ServiceStatusEnum.SERVICESTATUS_UNDEFINED
    PLANNED         = ServiceStatusEnum.SERVICESTATUS_PLANNED
    ACTIVE          = ServiceStatusEnum.SERVICESTATUS_ACTIVE
    PENDING_REMOVAL = ServiceStatusEnum.SERVICESTATUS_PENDING_REMOVAL

grpc_to_enum__service_status = functools.partial(
    grpc_to_enum, ServiceStatusEnum, ORM_ServiceStatusEnum)

class ServiceModel(Base):
    __tablename__ = 'Service'

    # pk = PrimaryKeyField()
    # context_fk = ForeignKeyField(ContextModel)
    context_uuid = Column(UUID(as_uuid=False), ForeignKey("Context.context_uuid"))
    # service_uuid = StringField(required=True, allow_empty=False)
    service_uuid = Column(UUID(as_uuid=False), primary_key=True, unique=True)
    # service_type = EnumeratedField(ORM_ServiceTypeEnum, required=True)
    service_type = Column(Enum(ORM_ServiceTypeEnum, create_constraint=False, native_enum=False, allow_empty=False))
    # service_constraints_fk = ForeignKeyField(ConstraintsModel)
    service_constraints = Column(UUID(as_uuid=False), ForeignKey("Constraints.constraints_uuid"))
    # service_status = EnumeratedField(ORM_ServiceStatusEnum, required=True)
    service_status = Column(Enum(ORM_ServiceStatusEnum, create_constraint=False, native_enum=False, allow_empty=False))
    # service_config_fk = ForeignKeyField(ConfigModel)
    service_config = Column(UUID(as_uuid=False), ForeignKey("Config.config_uuid"))

    # def delete(self) -> None:
    #     #pylint: disable=import-outside-toplevel
    #     from .RelationModels import ServiceEndPointModel
    #
    #     for db_service_endpoint_pk,_ in self.references(ServiceEndPointModel):
    #         ServiceEndPointModel(self.database, db_service_endpoint_pk).delete()
    #
    #     super().delete()
    #
    #     ConfigModel(self.database, self.service_config_fk).delete()
    #     ConstraintsModel(self.database, self.service_constraints_fk).delete()

    def main_pk_name(self):
        return 'context_uuid'


    def dump_id(self) -> Dict:
        context_id = ContextModel(self.database, self.context_fk).dump_id()
        return {
            'context_id': context_id,
            'service_uuid': {'uuid': self.service_uuid},
        }

    # def dump_endpoint_ids(self, endpoints) -> List[Dict]:
    #     from .RelationModels import ServiceEndPointModel # pylint: disable=import-outside-toplevel
    #     db_endpoints = get_related_objects(self, ServiceEndPointModel, 'endpoint_fk')
    #     return [db_endpoint.dump_id() for db_endpoint in sorted(db_endpoints, key=operator.attrgetter('pk'))]

    def dump_constraints(self) -> List[Dict]:
        return ConstraintsModel(self.database, self.service_constraints_fk).dump()

    def dump_config(self) -> Dict:
        return ConfigModel(self.database, self.service_config_fk).dump()

    def dump(   # pylint: disable=arguments-differ
            self, endpoint_ids=True, constraints=True, config_rules=True) -> Dict:
        result = {
            'service_id': self.dump_id(),
            'service_type': self.service_type.value,
            'service_status': {'service_status': self.service_status.value},
        }
        if endpoint_ids:
            result['service_endpoint_ids'] = self.dump_endpoint_ids()
        if constraints:
            result['service_constraints'] = self.dump_constraints()
        if config_rules:
            result.setdefault('service_config', {})['config_rules'] = self.dump_config()
        return result
