import json
import logging
from decimal import Decimal

from maps_adv.common.yt_utils import BaseImportWithYqlTask
from maps_adv.config_loader import Config
from maps_adv.geosmb.landlord.server.lib.data_manager import BaseDataManager
from maps_adv.geosmb.landlord.server.lib.domain import Domain

from . import yqls


class ImportPromosTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_promos_from_yt"
    YQL = yqls.import_promotions

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                source_table=config["PROMOS_YT_TABLE"],
                yt_cluster=config["YT_CLUSTER"],
                geadv_orgs_yt_table=config["GEOADV_ORGS_YT_TABLE"],
                geoadv_campaigns_yt_table=config["GEOADV_CAMPAIGNS_YT_TABLE"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return (
            row[0],
            row[1],
            str(row[2]),
            str(row[3]),
            str(row[4]),
            str(row[5]),
            str(row[6]) if row[6] else None,
            str(row[7]) if row[7] else None,
        )


class ImportPromotedCtaTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_promoted_cta_from_yt"
    YQL = yqls.import_ctas

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                source_table=config["PROMOS_YT_TABLE"],
                yt_cluster=config["YT_CLUSTER"],
                geadv_orgs_yt_table=config["GEOADV_ORGS_YT_TABLE"],
                geoadv_campaigns_yt_table=config["GEOADV_CAMPAIGNS_YT_TABLE"],
                base_widget_request_url=config["BASE_WIDGET_REQUEST_URL"],
                geoadv_base_yt_prefix=config["GEOADV_BASE_YT_PREFIX"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return (row[0], row[1], str(row[2]), str(row[3]))


class ImportPromotedServiceListsTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_promoted_service_lists_from_yt"
    YQL = yqls.import_service_lists

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                source_table=config["PROMOS_YT_TABLE"],
                yt_cluster=config["YT_CLUSTER"],
                geadv_orgs_yt_table=config["GEOADV_ORGS_YT_TABLE"],
                geoadv_campaigns_yt_table=config["GEOADV_CAMPAIGNS_YT_TABLE"],
            ),
            data_consumer=dm,
        )


class ImportPromotedServicesTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_promoted_services_from_yt"
    YQL = yqls.import_services

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                source_table=config["PROMOS_YT_TABLE"],
                yt_cluster=config["YT_CLUSTER"],
                geadv_orgs_yt_table=config["GEOADV_ORGS_YT_TABLE"],
                geoadv_campaigns_yt_table=config["GEOADV_CAMPAIGNS_YT_TABLE"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return (
            row[0],
            row[1],
            str(row[2]),
            Decimal(str(row[3]).replace(" ", "").replace(",", ".")) if row[3] else None,
            str(row[4]) if row[4] else None,
            str(row[5]) if row[5] else None,
            str(row[6]) if row[6] else None,
        )


class ImportCallTrackingTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_call_tracking_from_yt"
    YQL = yqls.import_call_tracking

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                source_table=config["PROMOS_YT_TABLE"],
                yt_cluster=config["YT_CLUSTER"],
                geadv_orgs_yt_table=config["GEOADV_ORGS_YT_TABLE"],
                geoadv_campaigns_yt_table=config["GEOADV_CAMPAIGNS_YT_TABLE"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return (row[0], row[1], str(row[2]))


async def update_landing_config_from_bunker(*args, domain: Domain, **kwargs):
    await domain.update_landing_config()


class ImportGoogleCountersTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_google_counters_from_yt"
    YQL = yqls.import_google_counters

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                source_table=config["YT_GOOGLE_COUNTERS_TABLE"],
                yt_cluster="senecaman",
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return row[0], json.dumps([{"id": struct.id, "goals": struct.goals} for struct in row[1]])


class ImportAvatarsTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_avatars_from_yt"
    YQL = yqls.import_avatars

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                yt_cluster=config["YT_CLUSTER"],
                geoadv_base_yt_prefix=config["GEOADV_BASE_YT_PREFIX"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return row[0], int(row[1]), row[2]


class ImportMarketIntServicesTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_market_int_services_from_yt"
    YQL = yqls.import_market_int_services

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                yt_cluster=config["YT_CLUSTER"],
                market_int_streaming_folder=config["MARKET_INT_STREAMING_YT_FOLDER"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        try:
            return (
                row[0],  # service id
                row[1],  # biz_id
                json.dumps(
                    {
                        "id": row[0],
                        "name": cls._decode_str(row[2]),
                        "description": cls._decode_str(row[3]),
                        "categories": [cls._decode_str(category) for category in row[4]] if row[4] is not None else [],
                        "min_cost": row[5],
                        "min_duration": row[6],
                        "action_type": row[7],
                        "image": cls._add_image_postfix(row[8]),
                    }
                ),
            )
        except Exception as e:
            logging.getLogger(__name__).error(f"Unable to convert row {row}", exc_info=e)
            raise e

    @classmethod
    def _add_image_postfix(cls, url: str) -> str:
        """Add %s postfix to avatar url templates if missing"""
        if url is not None and not url.endswith("/%s"):
            url += "/%s"

        return url

    @classmethod
    def _decode_str(cls, value: str) -> str:
        return value.decode("ISO-8859-1") if isinstance(value, bytes) else value


async def update_geosearch_data(*args, domain: Domain, **kwargs):
    await domain.update_geosearch_data()


async def push_landing_urls_to_ext_feed_lb(*args, domain: Domain, **kwargs):
    await domain.push_landing_urls_to_ext_feed_lb()


class ImportTikTokPixelsTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_tiktok_pixels_from_yt"
    YQL = yqls.import_tiktok_pixels

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                source_table=config["YT_TIKTOK_PIXELS_TABLE"],
                yt_cluster=config["YT_CLUSTER"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return row[0], json.dumps([{"id": struct.id, "goals": struct.goals} for struct in row[1]])


class ImportGoodsDataTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_goods_data_from_yt"
    YQL = yqls.import_goods_data

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                yt_goods_table=config["YT_GOODS_TABLE"],
                yt_biz_state_table=config["YT_BIZ_STATE_TABLE"],
                yt_cluster=config["YT_CLUSTER"],
                source="TYCOON",
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        try:
            return row[0], json.dumps(
                {
                    "categories": [{"name": cls._decode_str(category)} for category in row[1]],
                    "source_name": "TYCOON",
                }
            )
        except Exception as e:
            logging.getLogger("ImportGoodsDataTask").error(f"GOOD CATEGORIES error for permalink {row[0]}")
            logging.getLogger("ImportGoodsDataTask").error(f"GOOD CATEGORIES error for permalink {row[0]}: {row[1]}")
            raise e

    @classmethod
    def _decode_str(cls, value: str) -> str:
        return value.decode("ISO-8859-1") if isinstance(value, bytes) else value


class ImportVkPixelsTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "import_vk_pixels_from_yt"
    YQL = yqls.import_vk_pixels

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                source_table=config["YT_VK_PIXELS_TABLE"],
                yt_cluster=config["YT_CLUSTER"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return row[0], json.dumps([{"id": struct.id, "goals": struct.goals} for struct in row[1]])


class SyncPermalinksTask(BaseImportWithYqlTask):
    CHUNKED_WRITER_METHOD_NAME = "sync_permalinks_from_yt"
    YQL = yqls.sync_permalinks

    def __init__(self, *args, config: Config, dm: BaseDataManager, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                yt_cluster=config["YT_CLUSTER"],
                geoadv_base_yt_prefix=config["GEOADV_BASE_YT_PREFIX"],
                landlord_base_yt_prefix=config["LANDLORD_BASE_YT_PREFIX"],
            ),
            data_consumer=dm,
        )

    @classmethod
    def _yt_row_decode(cls, row: tuple) -> tuple:
        return row[0], row[1]
