# Copyright 2022-2023 ETSI TeraFlowSDN - TFS OSG (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 json
import logging
import time
from decimal import ROUND_HALF_EVEN, Decimal
from flask.json import jsonify
from common.proto.context_pb2 import (
    ContextId, Empty, EndPointId, ServiceId, ServiceTypeEnum, Service, Constraint, Constraint_SLA_Capacity,
    ConfigRule, ConfigRule_Custom, ConfigActionEnum)
from common.tools.grpc.Tools import grpc_message_to_json
from common.tools.object_factory.Context import json_context_id
from common.tools.object_factory.Service import json_service_id

LOGGER = logging.getLogger(__name__)


def service_2_bwInfo(service: Service) -> dict:
    response = {}
    # allocationDirection = '??' # String: 00 = Downlink (towards the UE); 01 = Uplink (towards the application/session); 10 = Symmetrical
    response['appInsId'] = service.service_id.service_uuid.uuid # String: Application instance identifier
    for constraint in service.service_constraints:
        if constraint.WhichOneof('constraint') == 'sla_capacity':
            # String: Size of requested fixed BW allocation in [bps]
            fixed_allocation = Decimal(constraint.sla_capacity.capacity_gbps * 1.e9)
            fixed_allocation = fixed_allocation.quantize(Decimal('0.1'), rounding=ROUND_HALF_EVEN)
            response['fixedAllocation'] = str(fixed_allocation)
            break

    for config_rule in service.service_config.config_rules:
        for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'sourceIp', 'sourcePort', 'dstPort', 'protocol', 'sessionFilter']:
            if config_rule.custom.resource_key == key:
                if key != 'sessionFilter':
                    response[key] = config_rule.custom.resource_value
                else:
                    response[key] = json.loads(config_rule.custom.resource_value)

    unixtime = time.time()
    response['timeStamp'] = { # Time stamp to indicate when the corresponding information elements are sent
        "seconds": int(unixtime),
        "nanoseconds": int(unixtime%1*1e9)
    }

    return response

def bwInfo_2_service(client, bwInfo: dict) -> Service:
    service = Service()
    for key in ['allocationDirection', 'fixedBWPriority', 'requestType', 'timeStamp', 'sessionFilter']:
        if key not in bwInfo:
            continue
        config_rule = ConfigRule()
        config_rule.action = ConfigActionEnum.CONFIGACTION_SET
        config_rule_custom = ConfigRule_Custom()
        config_rule_custom.resource_key  = key
        if key != 'sessionFilter':
            config_rule_custom.resource_value  = str(bwInfo[key])
        else:
            config_rule_custom.resource_value  = json.dumps(bwInfo[key])
        config_rule.custom.CopyFrom(config_rule_custom)
        service.service_config.config_rules.append(config_rule)

    if 'sessionFilter' in bwInfo:
        a_ip = bwInfo['sessionFilter'][0]['sourceIp']
        z_ip = bwInfo['sessionFilter'][0]['dstAddress']

        devices = client.ListDevices(Empty()).devices
        for device in devices:
            for cr in device.device_config.config_rules:
                if cr.WhichOneof('config_rule') == 'custom' and cr.custom.resource_key == '_connect/settings':
                    for ep in json.loads(cr.custom.resource_value)['endpoints']:
                        if 'ip' in ep and (ep['ip'] == a_ip or ep['ip'] == z_ip):
                            ep_id = EndPointId()
                            ep_id.endpoint_uuid.uuid = ep['uuid']
                            ep_id.device_id.device_uuid.uuid = device.device_id.device_uuid.uuid
                            service.service_endpoint_ids.append(ep_id)

    service.service_type = ServiceTypeEnum.SERVICETYPE_L3NM

    if 'appInsId' in bwInfo:
        service.service_id.service_uuid.uuid = bwInfo['appInsId']
        service.service_id.context_id.context_uuid.uuid = 'admin'
        service.name = bwInfo['appInsId']

    if 'fixedAllocation' in bwInfo:
        capacity = Constraint_SLA_Capacity()
        capacity.capacity_gbps = float(bwInfo['fixedAllocation']) / 1.e9
        constraint = Constraint()
        constraint.sla_capacity.CopyFrom(capacity)
        service.service_constraints.append(constraint)

    return service


def format_grpc_to_json(grpc_reply):
    return jsonify(grpc_message_to_json(grpc_reply))

def grpc_context_id(context_uuid):
    return ContextId(**json_context_id(context_uuid))

def grpc_service_id(context_uuid, service_uuid):
    return ServiceId(**json_service_id(service_uuid, context_id=json_context_id(context_uuid)))
