import urllib
from typing import Any, Dict, List, Literal, Optional

from maps_adv.geosmb.clients.geosearch import (
    AddressComponent,
    GeoSearchClient,
    GeoSearchOrgResult,
)
from maps_adv.geosmb.landlord.server.lib.data_manager import BaseDataManager
from maps_adv.geosmb.landlord.server.lib.enums import LandingVersion
from maps_adv.geosmb.landlord.server.lib.exceptions import UnsupportedFieldForSuggest


class Suggester:
    __slots__ = (
        "_dm",
        "_geosearch",
        "_maps_url",
        "_widget_request_url",
    )

    _dm: BaseDataManager
    _geosearch: GeoSearchClient

    _maps_url: str
    __widget_request_url: str

    def __init__(
        self,
        dm: BaseDataManager,
        geosearch: GeoSearchClient,
        maps_url: str,
        widget_request_url: str,
    ):
        self._dm = dm
        self._geosearch = geosearch

        self._maps_url = maps_url
        self._widget_request_url = widget_request_url

    async def __call__(self, *, biz_id: int, field: str) -> List[Any]:
        try:
            getter = getattr(self, field)
        except AttributeError:
            raise UnsupportedFieldForSuggest(field)

        return await getter(biz_id)

    async def categories(self, biz_id: int) -> List[str]:
        data = await self._dm.fetch_landing_data_for_crm(
            biz_id=biz_id, version=LandingVersion.STABLE
        )
        permalink = int(data["landing_details"]["contacts"]["geo"]["permalink"])

        orginfo = await self._geosearch.resolve_org(permalink=permalink)
        if not orginfo:
            return data["landing_details"]["categories"]
        return orginfo.categories_names

    async def plain_extras(self, biz_id: int) -> List[str]:
        data = await self._dm.fetch_landing_data_for_crm(
            biz_id=biz_id, version=LandingVersion.STABLE
        )
        permalink = int(data["landing_details"]["contacts"]["geo"]["permalink"])

        orginfo = await self._geosearch.resolve_org(permalink=permalink)
        if not orginfo:
            return data["landing_details"]["extras"]["plain_extras"]

        return list(
            feature["name"].capitalize()
            for feature in orginfo.features
            if "name" in feature and feature["value"] is True
        )

    async def cta_button(
        self,
        biz_id: int,
        landing_details: Optional[Dict[str, Any]] = None,
        orginfo: Optional[GeoSearchOrgResult] = None,
    ) -> Dict[Literal["available_buttons", "available_types"], Any]:
        if not landing_details:
            landing_data = await self._dm.fetch_landing_data_for_crm(
                biz_id, LandingVersion.UNSTABLE
            )
            landing_details = landing_data["landing_details"]

        permalink = int(landing_details["contacts"]["geo"]["permalink"])

        if not orginfo:
            orginfo = await self._geosearch.resolve_org(permalink=permalink)

        if orginfo:
            permalink = int(orginfo.permalink)

        buttons, types = [], []

        # Detect CALL button
        types.append("CALL")
        phone = landing_details["contacts"].get("phone")
        if phone is not None:
            buttons.append(dict(predefined="CALL", value=phone))

        # Detect MAKE_ROUTE button
        geo = landing_details["contacts"]["geo"]
        address_is_accurate = geo.get(
            "address_is_accurate",
            # if address with house - consider it accurate
            orginfo and AddressComponent.HOUSE in orginfo.address_components,
        )
        if orginfo and not orginfo.is_online and address_is_accurate:
            types.append("MAKE_ROUTE")
            address = geo.get("address", orginfo.formatted_address)
            if address is not None:
                lat = geo["lat"] if "lat" in geo else orginfo.latitude
                lon = geo["lon"] if "lon" in geo else orginfo.longitude
                params = [
                    ("ll", f"{lon},{lat}"),
                    ("rtext", f"~{lat},{lon}"),
                    ("mode", "route"),
                    ("ruri", f"ymapsbm1://org?oid={permalink}"),
                ]
                buttons.append(
                    dict(
                        predefined="MAKE_ROUTE",
                        value=f"{self._maps_url}?{urllib.parse.urlencode(params)}",
                    )
                )

        # Detect BOOK_MAPS button
        if orginfo:
            if orginfo.bookings:
                types.append("BOOK_MAPS")
                buttons.append(
                    dict(predefined="BOOK_MAPS", value=orginfo.bookings[0]["standaloneWidgetPath"])
                )

        # Detect REQUEST button
        types.append("REQUEST")
        buttons.append(
            dict(predefined="REQUEST", value=f"{self._widget_request_url}/{permalink}")
        )

        return {"available_buttons": buttons, "available_types": types}

    async def cta_button_by_data(
        self,
        permalink: int,
        landing_details: Dict[str, Any],
    ) -> Dict[Literal["available_buttons", "available_types"], Any]:

        buttons, types = [], []

        # Detect CALL button
        phone = landing_details["contacts"].get("phone")
        if phone is not None:
            types.append("CALL")
            buttons.append(dict(predefined="CALL", value=phone))

        # Detect MAKE_ROUTE button
        geo = landing_details["contacts"].get("geo")
        if geo is not None:
            address_is_accurate = geo.get("address_is_accurate")
            if address_is_accurate:
                address = geo.get("address")
                if address is not None:
                    lat = geo["lat"]
                    lon = geo["lon"]
                    params = [
                        ("ll", f"{lon},{lat}"),
                        ("rtext", f"~{lat},{lon}"),
                        ("mode", "route"),
                        ("ruri", f"ymapsbm1://org?oid={permalink}"),
                    ]
                    types.append("MAKE_ROUTE")
                    buttons.append(
                        dict(
                            predefined="MAKE_ROUTE",
                            value=f"{self._maps_url}?{urllib.parse.urlencode(params)}",
                        )
                    )

        # Detect REQUEST button
        types.append("REQUEST")
        buttons.append(
            dict(predefined="REQUEST", value=f"{self._widget_request_url}/{permalink}")
        )

        return {"available_buttons": buttons, "available_types": types}
