# 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 the HMC Administrative Places.
"""
from dataclasses import dataclass
from typing import List, Mapping, Optional

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.hmc2.place_context import PlaceClassification, PlaceContext
from here.content.utils.proto import decode_enum, decode_enum_array, decode_message, parse_ref
from here.platform.adapter import DecodedMessage, Identifier, Partition, Ref, make_cat_ref


@dataclass
class Place:
    """Administrative Place."""

    partition_id: Partition
    place_id: Identifier
    name: List[DecodedMessage]
    category: List[Ref]
    alt_category: List[Ref]
    location_ref: Ref
    valid_unnamed: bool

    # Additional optional properties
    daylight_saving_time: Optional[DecodedMessage] = None
    government_code: Optional[int] = None
    administrative_restriction: Optional[DecodedMessage] = None
    time_zone: Optional[str] = None
    country: Optional[DecodedMessage] = None
    capital_indicator: Optional[DecodedMessage] = None
    population: Optional[int] = None

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


class AdminPlaces(SingleLayerBinding):
    """
    Bindings for the HMC Administrative Places.
    """

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

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

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

        place = [
            Place(
                partition_id=Partition(partition_id),
                place_id=Identifier(p.identifier),
                name=[decode_message(n) for n in p.name],
                category=[make_cat_ref(c.partition_name, c.identifier) for c in p.category],
                alt_category=[
                    make_cat_ref(c.partition_name, c.identifier) for c in p.alt_category
                ],
                location_ref=parse_ref(p.location_ref),
                valid_unnamed=bool(p.valid_unnamed),
            )
            for p in msg.place  # type: ignore
        ]

        # Adding optional properties
        for x in msg.daylight_saving_time:  # type: ignore
            dst = decode_message(x.daylight_saving_time)
            for place_index in x.place_index:
                place[place_index].daylight_saving_time = dst
        for x in msg.government_code:  # type: ignore
            for place_index in x.place_index:
                place[place_index].government_code = x.government_code
        for x in msg.country:  # type: ignore
            restriction = decode_message(x, ignore=["place_index"])
            for place_index in x.place_index:
                place[place_index].administrative_restriction = restriction
        for x in msg.time_zone:  # type: ignore
            for place_index in x.place_index:
                place[place_index].time_zone = x.time_zone
        for x in msg.country:  # type: ignore
            country = decode_message(x, ignore=["place_index"])
            for place_index in x.place_index:
                place[place_index].country = country
        for x in msg.capital_indicator:  # type: ignore
            capital = decode_message(x, ignore=["place_index"])
            for place_index in x.place_index:
                place[place_index].capital_indicator = capital
        for x in msg.population:  # type: ignore
            for place_index in x.place_index:
                place[place_index].population = int(x.population)

        def parse_classifications(cs) -> List[PlaceClassification]:
            return [
                PlaceClassification(
                    preference=decode_enum(c, "preference"), place_ref=parse_ref(c.place_ref)
                )
                for c in cs
            ]

        context = [
            PlaceContext(
                classification_type=decode_enum(ctx, "classification_type"),
                city=parse_classifications(ctx.city),
                postal_code=parse_classifications(ctx.postal_code),
                country=parse_classifications(ctx.country),
                state=parse_classifications(ctx.state),
                region=parse_classifications(ctx.region),
                county=parse_classifications(ctx.county),
                district=parse_classifications(ctx.district),
                sub_district=parse_classifications(ctx.sub_district),
                further_zone=parse_classifications(ctx.further_zone),
                default_mapping=int(ctx.default_mapping),
                political_view=decode_enum_array(ctx, "political_view", False),
                political_view_exception=decode_enum_array(ctx, "political_view_exception", False),
            )
            for ctx in msg.administrative_context  # type: ignore
        ]
        return ContentPartition(cls, place=place, context=context)
