from datetime import timedelta
from decimal import Decimal

import pytest
from yandex.maps.proto.common2 import attribution_pb2, geo_object_pb2, geometry_pb2
from yandex.maps.proto.search import (
    address_pb2,
    business_images_pb2,
    business_pb2,
    business_rating_pb2,
    experimental_pb2,
    hours_pb2,
    kind_pb2,
    metrika_pb2,
    photos_2x_pb2,
)

from maps_adv.common.helpers import coro_mock
from maps_adv.geosmb.clients.geosearch import AddressComponent, GeoSearchClient

pytest_plugins = ["smb.common.aiotvm.pytest.plugin"]


class GeoSearchOrgResultMock:
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    def __setitem__(self, key, value):
        self.__dict__[key] = value


@pytest.fixture
def _business_go_meta():
    return business_pb2.GeoObjectMetadata(
        id="54321",
        name="Кафе",
        address=address_pb2.Address(
            formatted_address="Россия, Область, Город, Улица, 1",
            country_code="RU",
            postal_code="1234567",
            component=[
                address_pb2.Component(name="Область", kind=[kind_pb2.Kind.PROVINCE]),
                address_pb2.Component(name="Город", kind=[kind_pb2.Kind.LOCALITY]),
                address_pb2.Component(name="Улица", kind=[kind_pb2.Kind.STREET]),
                address_pb2.Component(name="1", kind=[kind_pb2.Kind.HOUSE]),
            ],
        ),
        category=[
            business_pb2.Category(**{"name": "Общепит", "class": "eat"}),
            business_pb2.Category(**{"name": "Ресторан"}),
        ],
        phone=[
            business_pb2.Phone(
                type=business_pb2.Phone.Type.Value("PHONE"),
                formatted="+7 (495) 739-70-00",
                number=0,
                info="секретарь",
                details=business_pb2.Phone.Details(
                    country="7", prefix="495", number="7397000"
                ),
            ),
            business_pb2.Phone(
                type=business_pb2.Phone.Type.Value("FAX"),
                formatted="+7 (495) 739-70-11",
                number=0,
                info="секретарь-факс",
                details=business_pb2.Phone.Details(
                    country="7", prefix="495", number="7397011"
                ),
            ),
        ],
        open_hours=hours_pb2.OpenHours(
            hours=[
                hours_pb2.Hours(
                    day=[hours_pb2.DayOfWeek.Value("MONDAY")],
                    time_range=[
                        hours_pb2.TimeRange(**{"from": 9 * 60 * 60, "to": 18 * 60 * 60})
                    ],
                ),
                hours_pb2.Hours(
                    day=[hours_pb2.DayOfWeek.Value("TUESDAY")],
                    time_range=[
                        hours_pb2.TimeRange(**{"from": 9 * 60 * 60, "to": 18 * 60 * 60})
                    ],
                ),
            ],
            text="Текст про рабочее время",
            tz_offset=10800,
            state=hours_pb2.State(
                text="Сейчас оно работает",
                short_text="Да",
            ),
        ),
        link=[
            business_pb2.Link(
                link=attribution_pb2.Link(href="http://cafe.ru"),
                type=business_pb2.Link.Type.SELF,
                tag="self",
            ),
            business_pb2.Link(
                link=attribution_pb2.Link(href="http://haircut.com"),
                type=business_pb2.Link.Type.SELF,
                tag="self",
            ),
            business_pb2.Link(
                link=attribution_pb2.Link(href="http://cafe.livejournal.com"),
                type=business_pb2.Link.Type.SOCIAL,
                tag="social",
                aref="#lj",
            ),
        ],
        feature=[
            business_pb2.Feature(
                id="feature_11",
                name="Оплата картой",
                value=business_pb2.Feature.Value(boolean_value=True),
                aref="#yes.ru",
            ),
            business_pb2.Feature(
                id="feature_22", value=business_pb2.Feature.Value(boolean_value=False)
            ),
        ],
        snippet=business_pb2.Snippet(feature_ref=["feature_11"]),
        properties=business_pb2.Properties(
            item=[
                business_pb2.Properties.Item(key="prop_key1", value="prop_value1"),
                business_pb2.Properties.Item(key="prop_key2", value="prop_value2"),
            ]
        ),
        geocode_result=business_pb2.GeocodeResult(),
    )


@pytest.fixture
def _photos_2x_go_meta():
    return photos_2x_pb2.GeoObjectMetadata(
        count=15,
        photo=[
            photos_2x_pb2.Photo(url_template="https://images.ru/tpl1/%s"),
            photos_2x_pb2.Photo(url_template="https://images.ru/tpl2/%s"),
        ],
    )


@pytest.fixture
def _business_images_go_meta():
    return business_images_pb2.GeoObjectMetadata(
        logo=business_images_pb2.Logo(url_template="https://images.ru/logo/%s")
    )


@pytest.fixture
def _business_rating_go_meta():
    return business_rating_pb2.GeoObjectMetadata(ratings=10, reviews=5, score=3.5)


@pytest.fixture()
def _metrika_go_meta():
    return metrika_pb2.MetrikaMetadata(counter="counter_number_1")


@pytest.fixture
def _experimental_go_meta():
    return experimental_pb2.ExperimentalMetadata(
        experimental_storage=experimental_pb2.ExperimentalStorage(
            item=[
                experimental_pb2.ExperimentalStorage.Item(
                    key="data_vendor", value="yandex"
                )
            ]
        )
    )


@pytest.fixture
def _geo_object(
    _business_go_meta,
    _business_images_go_meta,
    _business_rating_go_meta,
    _photos_2x_go_meta,
    _metrika_go_meta,
    _experimental_go_meta,
):
    go = geo_object_pb2.GeoObject(
        geometry=[geometry_pb2.Geometry(point=geometry_pb2.Point(lat=11.22, lon=22.33))]
    )

    metas = {
        business_pb2.GEO_OBJECT_METADATA: _business_go_meta,
        photos_2x_pb2.GEO_OBJECT_METADATA: _photos_2x_go_meta,
        business_images_pb2.GEO_OBJECT_METADATA: _business_images_go_meta,
        business_rating_pb2.GEO_OBJECT_METADATA: _business_rating_go_meta,
        metrika_pb2.GEO_OBJECT_METADATA: _metrika_go_meta,
        experimental_pb2.GEO_OBJECT_METADATA: _experimental_go_meta,
    }

    for key, value in metas.items():
        metadata = go.metadata.add()
        metadata.Extensions[key].CopyFrom(value)

    return go


@pytest.fixture
def geosearch_resp(_geo_object):
    metas = {}
    ext_map = {
        business_pb2.GEO_OBJECT_METADATA: "business",
        photos_2x_pb2.GEO_OBJECT_METADATA: "photos",
        business_images_pb2.GEO_OBJECT_METADATA: "images",
        business_rating_pb2.GEO_OBJECT_METADATA: "rating",
        metrika_pb2.GEO_OBJECT_METADATA: "metrika",
        experimental_pb2.GEO_OBJECT_METADATA: "experimental",
    }
    for meta in _geo_object.metadata:
        for ext_name, local_name in ext_map.items():
            if meta.HasExtension(ext_name):
                metas.setdefault(local_name, meta.Extensions[ext_name])

    return GeoSearchOrgResultMock(
        geo_object=_geo_object,
        metas=metas,
        permalink="54321",
        name="Кафе с едой",
        categories_names=["Общепит", "Ресторан"],
        address_components={
            AddressComponent.PROVINCE: "Область",
            AddressComponent.LOCALITY: "Город",
            AddressComponent.STREET: "Улица",
            AddressComponent.HOUSE: "1",
        },
        formatted_address="Город, Улица, 1",
        country_code="RU",
        postal_code="1234567",
        formatted_phones=[
            "+7 (495) 739-70-00",
            "+7 (495) 739-70-11",
            "+7 (495) 739-70-22",
        ],
        formatted_callable_phones=["+7 (495) 739-70-00", "+7 (495) 739-70-22"],
        links=[
            "http://cafe.ru",
            "http://vk.com/cafe",
            "http://haircut.com",
            "http://cafe.livejournal.com",
        ],
        own_links=["http://cafe.ru", "http://haircut.com"],
        social_links={
            "vkontakte": "http://vk.com/cafe",
            "lj": "http://cafe.livejournal.com",
        },
        bookings=[],
        tz_offset=timedelta(seconds=10800),
        open_hours=[(32400, 64800), (118800, 151200)],
        coords=(Decimal("11.22"), Decimal("22.33")),
        latitude=Decimal("11.22"),
        longitude=Decimal("22.33"),
        photos=[
            {"id": "1", "url": "https://images.ru/tpl1/%s"},
            {"id": "2", "url": "https://images.ru/tpl2/%s"},
        ],
        logo="https://images.ru/logo/%s",
        cover="https://images.ru/tpl1/%s",
        rating={"ratings": 10, "reviews": 5, "score": Decimal("3.5")},
        features=[
            {"id": "feature_22", "value": ["one", "two"]},
            {
                "id": "feature_11",
                "name": "Оплата картой",
                "value": True,
                "aref": "#yes.ru",
            },
        ],
        snippet_features=[{"id": "feature_22", "value": ["one", "two"]}],
        is_online=False,
        permalink_moved_to=None,
        metrika_counter="counter_number_1",
        service_area=None,
        chain_id=None,
        emails=["cafe@gmail.com"],
    )


@pytest.fixture
def geosearch_resp_2(_geo_object):
    metas = {}
    ext_map = {
        business_pb2.GEO_OBJECT_METADATA: "business",
        photos_2x_pb2.GEO_OBJECT_METADATA: "photos",
        business_images_pb2.GEO_OBJECT_METADATA: "images",
        business_rating_pb2.GEO_OBJECT_METADATA: "rating",
        metrika_pb2.GEO_OBJECT_METADATA: "metrika",
        experimental_pb2.GEO_OBJECT_METADATA: "experimental",
    }
    for meta in _geo_object.metadata:
        for ext_name, local_name in ext_map.items():
            if meta.HasExtension(ext_name):
                metas.setdefault(local_name, meta.Extensions[ext_name])

    return GeoSearchOrgResultMock(
        geo_object=_geo_object,
        metas=metas,
        permalink="65432",
        name="Парикмахерская",
        categories_names=["Парикмахерская", "Маникюр"],
        address_components={
            AddressComponent.PROVINCE: "Область",
            AddressComponent.LOCALITY: "Город",
            AddressComponent.STREET: "Проспект",
            AddressComponent.HOUSE: "2",
        },
        formatted_address="Город, Проспект, 2",
        country_code="RU",
        postal_code="2345678",
        formatted_phones=[
            "+7 (833) 654-20-00",
            "+7 (833) 654-20-11",
            "+7 (833) 654-20-22",
        ],
        formatted_callable_phones=["+7 (833) 654-20-00", "+7 (833) 654-20-22"],
        links=[
            "http://haircut.ru",
            "http://vk.com/haircut",
            "http://haircut.com",
            "http://haircut.livejournal.com",
        ],
        own_links=["http://haircut.ru", "http://haircut.com"],
        social_links={
            "vkontakte": "http://vk.com/haircut",
            "lj": "http://haircut.livejournal.com",
        },
        bookings=[],
        tz_offset=timedelta(seconds=10800),
        open_hours=[(32400, 64800), (118800, 151200)],
        coords=(Decimal("55.66"), Decimal("66.77")),
        latitude=Decimal("55.66"),
        longitude=Decimal("66.77"),
        photos=[
            {"id": "1", "url": "https://images.ru/tpl3/%s"},
            {"id": "2", "url": "https://images.ru/tpl4/%s"},
        ],
        logo="https://images.ru/logo/%s",
        cover="https://images.ru/tpl3/%s",
        rating={"ratings": 50, "reviews": 10, "score": Decimal("3.7")},
        features=[
            {"id": "feature_22", "value": ["one", "two"]},
            {
                "id": "feature_11",
                "name": "Оплата картой",
                "value": True,
                "aref": "#yes.ru",
            },
        ],
        snippet_features=[{"id": "feature_22", "value": ["one", "two"]}],
        is_online=False,
        permalink_moved_to=None,
        metrika_counter="counter_number_2",
        service_area=None,
        chain_id=7776,
        emails=["hair@mail.ru"],
    )


@pytest.fixture(autouse=True)
async def geosearch(mocker, aiotvm, geosearch_resp, geosearch_resp_2):
    mock = mocker.patch(
        "maps_adv.geosmb.clients.geosearch.GeoSearchClient.resolve_org", coro_mock()
    )

    mock.coro.return_value = geosearch_resp

    mock = mocker.patch(
        "maps_adv.geosmb.clients.geosearch.GeoSearchClient.resolve_orgs", coro_mock()
    )

    mock.coro.return_value = [geosearch_resp, geosearch_resp_2]

    async with GeoSearchClient(
        "http://geosearch.server", tvm=aiotvm, tvm_destination="geosearch"
    ) as client:
        yield client
