# 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 for HERE Real Time Traffic Common Attributes.
"""
from dataclasses import dataclass
from typing import List, Optional, Set

from google.protobuf.message import Message
from here.content.base import ContentIndexer
from here.content.utils.hmc_external_references import HMCExternalReferences
from here.content.utils.proto import decode_enum, decode_message, has_field
from here.platform.adapter import DecodedMessage, Identifier, Partition, Ref, make_ref


@dataclass
class TopologySegment:
    """Topology Segment."""

    topology_segment_id: List[Identifier]
    is_first_segment_in_driving_direction: bool
    start_offset: float
    end_offset: float
    topology_segment: Optional[DecodedMessage]
    map_version: Optional[str]


@dataclass
class TrafficAttributes:
    """Traffic Flow-Incidents."""

    id: Identifier
    original_id: Optional[Identifier]
    partition: Partition
    created_timestamp: Optional[int]
    updated_timestamp: Optional[int]
    expiry_timestamp: Optional[int]
    processing_timestamp: Optional[int]
    map_version: Optional[str]
    is_active: bool
    topology_segment: Optional[TopologySegment]
    supplementary_location_ref: Optional[DecodedMessage]
    topology_segment_refs: Optional[List[Ref]]

    indexer = ContentIndexer(
        partition="partition",
        identifier="id",
        refs={
            "topology_segment": lambda a: [
                s for s in a.topology_segment.topology_segment_refs  # type: ignore
            ],
        },
    )


def parse_topology_segment_refs(msg: Message, partition: str) -> Optional[List[Ref]]:
    """Parse topology segments.

    :param msg: the protobuf message containing the topology segment ref.
    :param partition: the partition containing the traffic item.
    :returns: a list of segment reference if any, ``None`` otherwise.
    """
    segment_refs: Set[Ref] = set()
    if has_field(msg, "topology_segment"):  # type: ignore
        segment_refs.union(
            set(
                [
                    make_ref(partition, f"here:cm:segment:{i}")
                    for i in msg.topology_segment.topology_segment_id  # type: ignore
                ]
            )
        )
    elif (
        has_field(msg, "supplementary_location_ref")
        and HMCExternalReferences.check_catalog_access()
    ):
        segment_refs.union(
            set(
                [
                    HMCExternalReferences.pvid_to_segment(partition, link.pvid)
                    for l_ref in msg.supplementary_location_ref.location_reference  # type: ignore
                    if decode_enum(l_ref, "location_type_key") == "LINKS"
                    and has_field(l_ref, "link_ref")
                    for link in l_ref.link_ref
                ]
            )
        )
    return list(segment_refs)


def parse_topology_segments(msg: Message) -> Optional[TopologySegment]:
    """Parse topology segments.

    :param msg: the protobuf message containing the ``TopologySegment``.
    :returns: an instance of ``TopologySegment``, if any, ``None`` otherwise.
    """
    if has_field(msg, "topology_segment"):
        segment = msg.topology_segment  # type: ignore
        return TopologySegment(
            topology_segment_id=[Identifier(i) for i in segment.topology_segment_id],
            is_first_segment_in_driving_direction=segment.is_first_segment_in_driving_direction,
            start_offset=segment.start_offset,
            end_offset=segment.end_offset,
            topology_segment=decode_message(segment.topology_segment)
            if has_field(segment, "topology_segment")
            else None,
            map_version=segment.map_version if has_field(segment, "map_version") else None,
        )
    else:
        return None
