from collections.abc import Mapping

from maps_adv.common.yt_utils import (
    BaseExecuteYqlTask,
    BaseReplicateYtTableTask,
    BaseYtExportTask,
)
from maps_adv.geosmb.crane_operator.server.lib.domains import CouponsDomain, PoiDomain


class OrgsWithCouponsYtExportTask(BaseYtExportTask):
    TABLE_SCHEMA = [
        dict(name="permalink", type="uint64", required=True),
        dict(name="showcase", type="any"),
    ]

    ITER_SIZE: int = 1000
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_organizations_with_coupons_for_export"

    def __init__(self, *args, config: Mapping, coupons_domain: CouponsDomain, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["ORGS_WITH_COUPONS_YT_EXPORT_TABLE"],
            data_producer=coupons_domain,
        )


class OrgsWithBookingYtExportTask(BaseYtExportTask):
    TABLE_SCHEMA = [
        dict(name="permalink", type="uint64", required=True),
        dict(name="booking_url", type="string", required=True),
    ]

    ITER_SIZE: int = 1000
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_organizations_with_booking_for_export"

    def __init__(self, *args, config: Mapping, coupons_domain: CouponsDomain, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["ORGS_WITH_BOOKING_YT_EXPORT_TABLE"],
            data_producer=coupons_domain,
        )


class LoyaltyItemsYtExportTask(BaseYtExportTask):
    TABLE_SCHEMA = [
        dict(name="client_id", type="uint64", required=True),
        dict(name="issued_at", type="timestamp", required=True),
        dict(name="id", type="uint64", required=True),
        dict(name="type", type="string", required=True),
        dict(name="data", type="any"),
    ]

    ITER_SIZE: int = 1000
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_loyalty_items_for_export"

    def __init__(self, *args, config: Mapping, coupons_domain: CouponsDomain, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["LOYALTY_ITEMS_YT_EXPORT_TABLE"],
            data_producer=coupons_domain,
        )


class BusinessCouponsYtExportTask(BaseYtExportTask):
    TABLE_SCHEMA = [
        dict(name="biz_id", type="uint64", required=True),
        dict(name="item_id", type="uint64", required=True),
        dict(name="title", type="string", required=True),
        dict(name="services", type="any", required=False),
        dict(name="products_description", type="string", required=False),
        dict(name="price", type="string", required=True),
        dict(name="currency", type="string", required=True),
        dict(name="discount", type="uint32", required=True),
        dict(name="discounted_price", type="string", required=True),
        dict(name="start_date", type="timestamp", required=True),
        dict(name="get_until_date", type="timestamp", required=True),
        dict(name="end_date", type="timestamp", required=True),
        dict(name="distribution", type="string", required=True),
        dict(name="moderation_status", type="string", required=True),
        dict(name="published", type="boolean", required=True),
        dict(name="payments_enabled", type="boolean", required=True),
        dict(name="conditions", type="string", required=False),
        dict(name="creator_login", type="string", required=False),
        dict(name="creator_uid", type="string", required=False),
        dict(name="created_at", type="timestamp", required=True),
        dict(name="cover_templates", type="any", required=False),
        dict(name="coupon_showcase_url", type="string", required=False),
        dict(name="meta", type="any", required=False),
    ]

    ITER_SIZE: int = 1000
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_business_coupons_for_export"

    def __init__(self, *args, config: Mapping, coupons_domain: CouponsDomain, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["BUSINESS_COUPONS_YT_EXPORT_TABLE"],
            data_producer=coupons_domain,
        )


class CouponPromotionsYtExportTask(BaseYtExportTask):
    TABLE_SCHEMA = [
        dict(name="advert_id", type="uint64", required=True),
        dict(name="coupon_id", type="uint64", required=True),
        dict(name="biz_id", type="uint64", required=False),
        dict(name="name", type="string", required=True),
        dict(name="date_from", type="string", required=True),
        dict(name="date_to", type="string", required=True),
        dict(name="description", type="string", required=True),
        dict(name="announcement", type="string", required=True),
        dict(name="image_url", type="string", required=True),
        dict(name="coupon_url", type="string", required=True),
    ]

    ITER_SIZE: int = 1000
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_coupon_promotions_for_export"

    def __init__(self, *args, config: Mapping, coupons_domain: CouponsDomain, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["COUPON_PROMOTIONS_YT_EXPORT_TABLE"],
            data_producer=coupons_domain,
        )


class ReplicateCouponPromotionsTableYtTask(BaseReplicateYtTableTask):
    def __init__(self, *args, config: Mapping, **kwargs):
        super().__init__(
            src_cluster=config["YT_CLUSTER"],
            target_cluster=config["YT_REPLICATION_CLUSTER"],
            token=config["YT_TOKEN"],
            src_table=config["COUPON_PROMOTIONS_YT_EXPORT_TABLE"],
            target_table=config["COUPON_PROMOTIONS_YT_EXPORT_TABLE"],
        )


async def export_coupon_promotions(
    *args, config: Mapping, coupons_domain: CouponsDomain, **kwargs
):
    export_task = CouponPromotionsYtExportTask(
        config=config, coupons_domain=coupons_domain
    )
    await export_task()

    replication_task = ReplicateCouponPromotionsTableYtTask(config=config)
    await replication_task()


class GenerateDataForSnippet(BaseExecuteYqlTask):
    YQL = """
        $size = Yson::From(AsStruct(300 AS height, 200 AS width));

        INSERT INTO {yt_cluster}.`{target_table}` WITH TRUNCATE
        SELECT coalesce(t1.permalink, t2.permalink) AS permalink,
        Yson::From(AsStruct(
                CASE WHEN t1.booking_url IS NOT NULL
                    THEN Yson::From(AsStruct($size AS size, t1.booking_url AS url))
                    ELSE NULL
                    END AS booking_widget,
                CASE WHEN Yson::Lookup(Yson::Parse(t2.showcase), 'value') IS NOT NULL
                    THEN Yson::From(AsStruct(
                        $size AS size,
                        Yson::Lookup(Yson::Parse(t2.showcase), 'value') AS url)
                    )
                    ELSE NULL
                    END AS coupon_widget
        )) AS showcase
        FROM {yt_cluster}.`{booking_widget_table}` AS t1
        FULL JOIN {yt_cluster}.`{coupons_widget_table}` AS t2
        USING (permalink);
    """

    def __init__(self, *args, config: Mapping, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                target_table=config["YT_TABLE_FOR_SNIPPET"],
                yt_cluster=config["YT_CLUSTER"],
                booking_widget_table=config["ORGS_WITH_BOOKING_YT_EXPORT_TABLE"],
                coupons_widget_table=config["ORGS_WITH_COUPONS_YT_EXPORT_TABLE"],
            ),
        )


async def export_coupons_to_review(*args, coupons_domain: CouponsDomain, **kwargs):
    await coupons_domain.export_coupons_to_review()


async def export_reviewed_to_loyalty(*args, coupons_domain: CouponsDomain, **kwargs):
    await coupons_domain.export_coupons_reviewed_to_loyalty()


class OrdersPoiYtExportTask(BaseYtExportTask):
    TABLE_SCHEMA = [
        dict(name="passport_uid", type="uint64", required=False),
        dict(name="experiment_tag", type="string", required=True),
        dict(name="permalink", type="uint64", required=True),
        dict(
            name="subscript",
            type_v3={
                "type_name": "dict",
                "key": "string",
                "value": "string",
            },
            required=True,
        ),
    ]
    SORTING_PARAMS = ["passport_uid", "experiment_tag", "permalink"]

    ITER_SIZE: int = None
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_orders_poi_data_for_export"

    def __init__(self, *args, config: Mapping, poi_domain: PoiDomain, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["YT_TABLE_FOR_ORDERS_POI"],
            data_producer=poi_domain,
        )


class CouponsWithSegmentsYtExportTask(BaseYtExportTask):
    TABLE_SCHEMA = [
        dict(name="biz_id", type="uint64", required=True),
        dict(name="coupon_id", type="uint64", required=True),
        dict(name="type", type="string", required=True),
        dict(
            name="segments",
            type_v3={"type_name": "list", "item": "string"},
            required=True,
        ),
        dict(name="percent_discount", type="uint32", required=False),
        dict(name="cost_discount", type="string", required=False),
        dict(name="currency", type="string", required=False),
        dict(
            name="poi_subscript",
            type_v3={
                "type_name": "dict",
                "key": "string",
                "value": "string",
            },
            required=True,
        ),
    ]

    ITER_SIZE: int = None
    CHUNKED_ITERATOR_METHOD_NAME: str = "iter_coupons_with_segments_for_export"

    def __init__(self, *args, config: Mapping, coupons_domain: CouponsDomain, **kwargs):
        super().__init__(
            cluster=config["YT_CLUSTER"],
            token=config["YT_TOKEN"],
            table=config["COUPONS_WITH_SEGMENTS_YT_EXPORT_TABLE"],
            data_producer=coupons_domain,
        )


class GenerateCouponsPoiDataTask(BaseExecuteYqlTask):
    YQL = """
        $client_coupons = (
            SELECT
                clients.biz_id AS biz_id,
                clients.passport_uid AS passport_uid,
                coupons.coupon_id AS coupon_id,
                coupons.poi_subscript AS poi_subscript
            FROM {yt_cluster}.`{clients_table}` AS clients
            JOIN {yt_cluster}.`{coupons_table}` AS coupons
                ON coupons.biz_id = clients.biz_id
            WHERE NOT SetIsDisjoint(ToSet(clients.segments), ToSet(coupons.segments))
                AND clients.passport_uid IS NOT NULL
        );

        $distinct_client_coupons = (
            SELECT DISTINCT
                biz_id,
                passport_uid,
                first_value(poi_subscript) OVER w AS subscript
                FROM $client_coupons
                WINDOW w AS (
                    PARTITION BY biz_id, passport_uid
                    ORDER BY coupon_id
                )
        );

        INSERT INTO {yt_cluster}.`{target_table}` WITH TRUNCATE
        SELECT
            gco.permalink AS permalink,
            '{poi_experiment_tag}' AS experiment_tag,
            dcc.passport_uid AS passport_uid,
            dcc.subscript AS subscript
        FROM $distinct_client_coupons AS dcc
        JOIN {yt_cluster}.`{geoadv_campaign_table}` AS gc ON dcc.biz_id=gc.id
        JOIN {yt_cluster}.`{geoadv_campaign_organization_table}` AS gco
            ON gco.campaign_id = gc.id;
    """

    def __init__(self, *args, config: Mapping, **kwargs):
        super().__init__(
            yql_token=config["YQL_TOKEN"],
            yql_format_kwargs=dict(
                yt_cluster=config["YT_CLUSTER"],
                target_table=config["YT_TABLE_FOR_COUPONS_POI"],
                clients_table=config["YT_TABLE_CLIENTS"],
                coupons_table=config["COUPONS_WITH_SEGMENTS_YT_EXPORT_TABLE"],
                geoadv_campaign_table=config["YT_TABLE_GEOADV_CAMPAIGNS"],
                geoadv_campaign_organization_table=config[
                    "YT_TABLE_GEOADV_CAMPAIGN_ORGS"
                ],
                poi_experiment_tag=config["COUPONS_POI_EXPERIMENT_TAG"],
            ),
        )
