# Copyright (C) 2022-2023 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 the HMC Administrative Locations.
"""

from dataclasses import dataclass
from typing import List, Mapping, Optional

from geojson import MultiPolygon, Point
from google.protobuf.message import Message
from here.content.base import ContentIndexer, ContentPartition, SingleLayerBinding
from here.content.content import hmc_schema_version
from here.content.utils.proto import (
    decode_enum,
    decode_message,
    has_field,
    parse_multi_polygon,
    parse_point,
)
from here.platform.adapter import DecodedMessage, Identifier, Partition


@dataclass
class Location:
    """Administrative Location"""

    partition_id: Partition
    location_id: Identifier
    location_type: str
    display_position: Point
    geometry: MultiPolygon
    address_relation: str
    area_type: str
    segment_anchor: DecodedMessage
    segment_anchor_side: str

    # Additional optional properties
    accessors: Optional[DecodedMessage]
    political_geometry: Optional[DecodedMessage]
    alternate_geometry: Optional[DecodedMessage]
    within_location_ref: Optional[DecodedMessage]
    bounding_box: Optional[DecodedMessage]
    level_info: Optional[DecodedMessage]

    indexer = ContentIndexer(partition="partition_id", identifier="location_id")


class AdminLocations(SingleLayerBinding):
    """
    Bindings for the HMC Administrative Locations.
    """

    @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:placelocation_v2:{hmc_schema_version}"]

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

    @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
        """
        assert hasattr(msg, "location")

        location = [
            Location(
                partition_id=Partition(partition_id),
                location_id=Identifier(loc.identifier),
                location_type=decode_enum(loc, "location_type"),
                display_position=parse_point(loc.display_position),
                geometry=parse_multi_polygon(loc.geometry) if has_field(loc, "geometry") else None,
                address_relation=decode_enum(loc, "address_relation"),
                area_type=decode_enum(loc, "area_type"),
                segment_anchor=decode_message(loc.segment_anchor),
                segment_anchor_side=decode_enum(loc, "segment_anchor_side"),
                accessors=[decode_message(a) for a in msg.accessors]  # type: ignore
                if has_field(msg, "accessors")
                else None,
                political_geometry=[decode_message(a) for a in loc.political_geometry],
                alternate_geometry=[decode_message(a) for a in loc.alternate_geometry],
                within_location_ref=decode_message(loc.within_location_ref),
                bounding_box=loc.bounding_box if loc.bounding_box else None,
                level_info=loc.level_information if loc.level_information else None,
            )
            for loc in msg.location  # type: ignore
        ]

        address = [decode_message(a) for a in msg.address]  # type: ignore
        postal_code = [decode_message(p) for p in msg.postal_code]  # type: ignore

        return ContentPartition(cls, address=address, location=location, postal_code=postal_code)
