# Copyright (C) 2021-2022 HERE Global B.V. and its affiliate(s).
# All rights reserved.
#
# This software and other materials contain proprietary information
# controlled by HERE and are protected by applicable copyright legislation.
# Any use and utilization of this software and other materials and
# disclosure to any third parties is conditional upon having a separate
# agreement with HERE for the access, use, utilization or disclosure of this
# software. In the absence of such agreement, the use of the software is not
# allowed.

"""
Bindings to combine speedlimit from navigation and advanced navigation attributes layers
"""
from dataclasses import dataclass
from typing import Dict, Iterable, List, Mapping, Optional

from deprecated.sphinx import deprecated
from google.protobuf.message import Message
from here.content.base import ContentIndexer, ContentPartition, SingleLayerBinding
from here.content.content import Content as ParentContent
from here.content.content import hmc_schema_version
from here.content.hmc2 import AdvancedNavigationAttributes
from here.content.hmc2.base_attributes import (
    Attribute,
    BaseAttributesMultiLayerBinding,
    SegmentRef,
)
from here.platform.adapter import Adapter, Partition


@dataclass
@deprecated(version="2.30.0", reason="please use class TopologyAttributes instead")
class ExtendedSpeedLimitAttributes:
    """Speed Limit attributes for segments from the combination of layers."""

    partition_id: Partition
    segment_ref: SegmentRef
    forward: bool
    backward: bool
    speed_limit: int
    source: str
    unlimited: bool

    indexer = ContentIndexer(
        partition="partition_id", refs={"segment": lambda a: a.segment_ref.ref}  # type: ignore
    )


@deprecated(version="2.30.0", reason="please use class TopologyAttributes instead")
class ExtendedSpeedLimit(BaseAttributesMultiLayerBinding):
    """
    Bindings to combine speedlimit from navigation and advanced navigation attributes layers.
    """

    _content: ParentContent

    def __init__(self, content: ParentContent, layer_id: str, adapter: Optional[Adapter] = None):
        """
        Instantiate a class Multilayer ExtendedSpeedLimit binding.
        Assign content to Class attribute _content.

        :param content: Content object that references a catalog at a fixed version
        :param layer_id: ID of the only layer that contains the content
        :param adapter: the ``Adapter`` to transform data between different representations,
            ``None`` to use the adapter defined in ``content``.
        """
        super().__init__(content, layer_id, adapter)
        ExtendedSpeedLimit._content = content

    @classmethod
    def schema_hrn(cls) -> List[str]:
        """
        :return: the HRN of schema this binding can parse and index
        """
        return [
            f"hrn:here:schema:::com.here.schema.rib:navigation-attributes_v2:{hmc_schema_version}"
        ]

    @classmethod
    def object_types(cls) -> Mapping[str, type]:
        """
        :return: the types of attributes supported by this binding
        """
        return {"extended_speed_limit": ExtendedSpeedLimitAttributes}

    @classmethod
    def _internal_object_types(cls) -> Mapping[str, type]:
        """
        :return: the types of attributes supported by this binding
        """
        return {"speed_limit": Attribute}

    @classmethod
    def associated_layers(cls) -> Dict[str, SingleLayerBinding]:
        """
        Provide associated dependent/independent layers to the base layer.

        :return: dict of key as single layer and value as SingleLayerBinding Object.
        """
        return {
            "advanced_navigation_attributes": AdvancedNavigationAttributes(
                cls._content, "advanced-navigation-attributes"
            )
        }

    @classmethod
    def parse_and_index(cls, partition_id: str, msg: Message) -> ContentPartition:
        """
        Parse and index decoded message.

        :param partition_id: identifier of the partition
        :param msg: decoded content from the layer, in the form of a Protobuf message
        :return: the content of a partition parsed and indexed in its own data structure
        """

        content_partition = super().parse_and_index(partition_id, msg)

        na_speed_limit = content_partition.objects("extended_speed_limit")

        extended_speed_limit: list = cls.process_advanced_attributes(na_speed_limit, partition_id)

        return ContentPartition(cls, extended_speed_limit=extended_speed_limit)

    @classmethod
    def _get_hashed_segment_key(cls, segment_ref: SegmentRef, forward: bool) -> str:
        """
        Generate hash key with the combination of SegmentRef and Direction of travel.

        :param segment_ref: instance of SegmentRef to be hashed
        :param forward: determines the direction of travel
        :return: the generated hash key
        """

        return f"{segment_ref.ref.partition}_{segment_ref.ref.identifier}_{forward}"

    @classmethod
    def process_advanced_attributes(
        cls, na_speed_limits: Iterable[object], partition_id: str
    ) -> List[ExtendedSpeedLimitAttributes]:
        """
        Process and combine advanced navigation attributes.

        :param na_speed_limits: a dict of segment to speed limit.
        :param partition_id: partition id of the base layer.
        :return: list of ExtendedSpeedLimitAttributes object.
        """

        advanced_navigation_attributes = cls.associated_layers()["advanced_navigation_attributes"]

        ana_speed_limits = advanced_navigation_attributes.get(
            partition=partition_id, object_type="speed_limit"
        )

        extended_speed_limit: Dict[str, ExtendedSpeedLimitAttributes] = {
            cls._get_hashed_segment_key(
                segment, nas.forward  # type: ignore
            ): ExtendedSpeedLimitAttributes(
                partition_id=partition_id,
                segment_ref=segment,
                forward=nas.forward,  # type: ignore
                backward=nas.backward,  # type: ignore
                speed_limit=nas.attribute["value"],  # type: ignore
                source=nas.attribute["source"],  # type: ignore
                unlimited=nas.attribute["unlimited"],  # type: ignore
            )
            for nas in na_speed_limits
            for segment in nas.segments  # type: ignore
        }

        for anas in ana_speed_limits:
            for segment in anas.segments:
                extended_speed_limit.update(
                    {
                        cls._get_hashed_segment_key(
                            segment, anas.forward
                        ): ExtendedSpeedLimitAttributes(
                            partition_id=partition_id,
                            segment_ref=segment,
                            forward=anas.forward,
                            backward=anas.backward,
                            speed_limit=anas.attribute["value"],
                            source=anas.attribute["source"],
                            unlimited=anas.attribute["unlimited"],
                        )
                    }
                )

        return list(extended_speed_limit.values())
