# Copyright 2022-2024 ETSI 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.

from typing import Dict, List, Tuple

from common.proto.context_pb2 import Link
from common.tools.object_factory.ConfigRule import (
    json_config_rule_delete,
    json_config_rule_set,
)
from context.client.ContextClient import ContextClient


def setup_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]:
    operation_type: str = json_settings["operation_type"]
    src_node_id: str = json_settings["src_node_id"]
    src_mgmt_ip_address: str = json_settings["src_mgmt_ip_address"]
    src_ac_node_id: str = json_settings["src_ac_node_id"]
    src_ac_ep_id: str = json_settings["src_ac_ep_id"]
    src_vlan: str = json_settings["src_vlan"]
    src_source_ip_prefix: str = json_settings["src_source_ip_prefix"]
    src_source_tcp_port: str = json_settings["src_source_tcp_port"]
    src_destination_ip_prefix: str = json_settings["src_destination_ip_prefix"]
    src_destination_tcp_port: str = json_settings["src_destination_tcp_port"]
    source_one_way_delay: int = int(json_settings["source_one_way_delay"])
    source_one_way_bandwidth: int = int(json_settings["source_one_way_bandwidth"])
    source_one_way_packet_loss: float = float(
        json_settings["source_one_way_packet_loss"]
    )
    dst_node_id: str = json_settings["dst_node_id"]
    dst_mgmt_ip_address: str = json_settings["dst_mgmt_ip_address"]
    dst_ac_node_id: str = json_settings["dst_ac_node_id"]
    dst_ac_ep_id: str = json_settings["dst_ac_ep_id"]
    dst_vlan: str = json_settings["dst_vlan"]
    dst_source_ip_prefix: str = json_settings["dst_source_ip_prefix"]
    dst_source_tcp_port: str = json_settings["dst_source_tcp_port"]
    dst_destination_ip_prefix: str = json_settings["dst_destination_ip_prefix"]
    dst_destination_tcp_port: str = json_settings["dst_destination_tcp_port"]
    destination_one_way_delay: int = int(json_settings["destination_one_way_delay"])
    destination_one_way_bandwidth: int = int(
        json_settings["destination_one_way_bandwidth"]
    )
    destination_one_way_packet_loss: float = float(
        json_settings["destination_one_way_packet_loss"]
    )
    slice_id: str = json_settings["slice_id"]

    sdps = [
        {
            "id": "1",
            "node-id": src_node_id,
            "sdp-ip-address": [src_mgmt_ip_address],
            "service-match-criteria": {
                "match-criterion": [
                    {
                        "index": 1,
                        "match-type": [
                            {
                                "type": "ietf-network-slice-service:vlan",
                                "value": [src_vlan],
                            },
                            {
                                "type": "ietf-network-slice-service:source-ip-prefix",
                                "value": [src_source_ip_prefix],
                            },
                            {
                                "type": "ietf-network-slice-service:source-tcp-port",
                                "value": [src_source_tcp_port],
                            },
                            {
                                "type": "ietf-network-slice-service:destination-ip-prefix",
                                "value": [src_destination_ip_prefix],
                            },
                            {
                                "type": "ietf-network-slice-service:destination-tcp-port",
                                "value": [src_destination_tcp_port],
                            },
                        ],
                        "target-connection-group-id": "line1",
                    }
                ]
            },
            "attachment-circuits": {
                "attachment-circuit": [
                    {
                        "id": "0",
                        "description": "dsc",
                        "ac-node-id": src_ac_node_id,
                        "ac-tp-id": src_ac_ep_id,
                    }
                ]
            },
        },
        {
            "id": "2",
            "node-id": dst_node_id,
            "sdp-ip-address": [dst_mgmt_ip_address],
            "service-match-criteria": {
                "match-criterion": [
                    {
                        "index": 1,
                        "match-type": [
                            {
                                "type": "ietf-network-slice-service:vlan",
                                "value": [dst_vlan],
                            },
                            {
                                "type": "ietf-network-slice-service:source-ip-prefix",
                                "value": [dst_source_ip_prefix],
                            },
                            {
                                "type": "ietf-network-slice-service:source-tcp-port",
                                "value": [dst_source_tcp_port],
                            },
                            {
                                "type": "ietf-network-slice-service:destination-ip-prefix",
                                "value": [dst_destination_ip_prefix],
                            },
                            {
                                "type": "ietf-network-slice-service:destination-tcp-port",
                                "value": [dst_destination_tcp_port],
                            },
                        ],
                        "target-connection-group-id": "line1",
                    }
                ]
            },
            "attachment-circuits": {
                "attachment-circuit": [
                    {
                        "id": "0",
                        "description": "dsc",
                        "ac-node-id": dst_ac_node_id,
                        "ac-tp-id": dst_ac_ep_id,
                    }
                ]
            },
        },
    ]

    connection_groups = [
        {
            "id": "line1",
            "connectivity-type": "point-to-point",
            "connectivity-construct": [
                {
                    "id": 1,
                    "p2p-sender-sdp": "1",
                    "p2p-receiver-sdp": "2",
                    "service-slo-sle-policy": {
                        "slo-policy": {
                            "metric-bound": [
                                {
                                    "metric-type": "ietf-network-slice-service:one-way-delay-maximum",
                                    "metric-unit": "milliseconds",
                                    "bound": source_one_way_delay,
                                },
                                {
                                    "metric-type": "ietf-network-slice-service:one-way-bandwidth",
                                    "metric-unit": "Mbps",
                                    "bound": source_one_way_bandwidth,
                                },
                                {
                                    "metric-type": "ietf-network-slice-service:two-way-packet-loss",
                                    "metric-unit": "percentage",
                                    "percentile-value": source_one_way_packet_loss,
                                },
                            ]
                        }
                    },
                },
                {
                    "id": 2,
                    "p2p-sender-sdp": "2",
                    "p2p-receiver-sdp": "1",
                    "service-slo-sle-policy": {
                        "slo-policy": {
                            "metric-bound": [
                                {
                                    "metric-type": "ietf-network-slice-service:one-way-delay-maximum",
                                    "metric-unit": "milliseconds",
                                    "bound": destination_one_way_delay,
                                },
                                {
                                    "metric-type": "ietf-network-slice-service:one-way-bandwidth",
                                    "metric-unit": "Mbps",
                                    "bound": destination_one_way_bandwidth,
                                },
                                {
                                    "metric-type": "ietf-network-slice-service:two-way-packet-loss",
                                    "metric-unit": "percentage",
                                    "percentile-value": destination_one_way_packet_loss,
                                },
                            ]
                        }
                    },
                },
            ],
        }
    ]
    slice_service = {
        "id": slice_id,
        "description": "dsc",
        "sdps": {"sdp": sdps},
        "connection-groups": {"connection-group": connection_groups},
    }
    slice_data_model = {"network-slice-services": {"slice-service": [slice_service]}}
    json_config_rules = [
        json_config_rule_set(
            "/service[{:s}]/IETFSlice".format(service_uuid),
            slice_data_model,
        ),
        json_config_rule_set(
            "/service[{:s}]/IETFSlice/operation".format(service_uuid),
            {"type": operation_type},
        ),
    ]
    return json_config_rules


def teardown_config_rules(service_uuid: str, json_settings: Dict) -> List[Dict]:
    json_config_rules = [
        json_config_rule_delete(
            "/service[{:s}]/IETFSlice".format(service_uuid),
            {},
        ),
        json_config_rule_delete(
            "/service[{:s}]/IETFSlice/operation".format(service_uuid),
            {},
        ),
    ]
    return json_config_rules


def get_link_ep_device_names(
    link: Link, context_client: ContextClient
) -> Tuple[str, str, str, str]:
    ep_ids = link.link_endpoint_ids
    ep_device_id_1 = ep_ids[0].device_id
    ep_uuid_1 = ep_ids[0].endpoint_uuid.uuid
    device_obj_1 = context_client.GetDevice(ep_device_id_1)
    for d_ep in device_obj_1.device_endpoints:
        if d_ep.endpoint_id.endpoint_uuid.uuid == ep_uuid_1:
            ep_name_1 = d_ep.name
            break
    else:
        raise Exception("endpoint not found")
    device_obj_name_1 = device_obj_1.name
    ep_device_id_2 = ep_ids[1].device_id
    ep_uuid_2 = ep_ids[1].endpoint_uuid.uuid
    device_obj_2 = context_client.GetDevice(ep_device_id_2)
    for d_ep in device_obj_2.device_endpoints:
        if d_ep.endpoint_id.endpoint_uuid.uuid == ep_uuid_2:
            ep_name_2 = d_ep.name
            break
    else:
        raise Exception("endpoint not found")
    device_obj_name_2 = device_obj_2.name
    return (
        device_obj_name_1,
        ep_name_1,
        device_obj_1,
        device_obj_name_2,
        ep_name_2,
        device_obj_2,
    )
