from textwrap import dedent
from datacloud.dev_utils.yt.yt_utils import get_yt_client, create_folders
from datacloud.dev_utils.yql.yql_helpers import execute_yql, create_yql_client
from datacloud.dev_utils.logging.logger import get_basic_logger


CATEGORIES_LIST = [
    "akvarium",
    "audio_i_video",
    "avtomobili",
    "bilety_i_puteshestviya",
    "bytovaya_tehnika",
    "chasy_i_ukrasheniya",
    "detskaya_odezhda_i_obuv",
    "doma_dachi_kottedzhi",
    "drugie_zhivotnye",
    "fototehnika",
    "garazhi_i_mashinomesta",
    "gotoviy_biznes",
    "gruzoviki_i_spetstehnika",
    "igry_pristavki_i_programmy",
    "knigi_i_zhurnaly",
    "kollektsionirovanie",
    "kommercheskaya_nedvizhimost",
    "komnaty",
    "koshki",
    "krasota_i_zdorove",
    "kvartiry",
    "mebel_i_interer",
    "mototsikly_i_mototehnika",
    "muzykalnye_instrumenty",
    "nastolnye_kompyutery",
    "noutbuki",
    "oborudovanie_dlya_biznesa",
    "odezhda_obuv_aksessuary",
    "ohota_i_rybalka",
    "orgtehnika_i_rashodniki",
    "planshety_i_elektronnye_knigi",
    "posuda_i_tovary_dlya_kuhni",
    "predlozheniya_uslug",
    "produkty_pitaniya",
    "ptitsy",
    "rasteniya",
    "remont_i_stroitelstvo",
    "rezume",
    "sobaki",
    "sport_i_otdyh",
    "telefony",
    "tovary_dlya_detey_i_igrushki",
    "tovary_dlya_kompyutera",
    "tovary_dlya_zhivotnyh",
    "vakansii",
    "velosipedy",
    "vodnyy_transport",
    "zapchasti_i_aksessuary",
    "zaprosy_na_uslugi",
    "zemelnye_uchastki",
]


CLOUD_NODES_PRAGMAS = dedent("""
    PRAGMA yt.PoolTrees = "physical";
    PRAGMA yt.TentativePoolTrees = "cloud";
""")


def build_contac_actions_vectors(yt_client, yql_client, build_config, logger=None):
    logger = logger or get_basic_logger(name=__name__, format='%(asctime)s %(message)s')
    yt_client = yt_client or get_yt_client()
    yql_client = yql_client or create_yql_client(yt_client)

    logger.info('Start calculate contact actions features')

    if hasattr(build_config, 'data_dir'):
        create_folders(build_config.data_dir, yt_client)

    yql_query = dedent("""
        %(custom_pragmas)s
        $days_to_take = %(days_to_take)s;
        $contact_log_dir = '%(contact_log_dir)s';
        $features_table_path = '%(features_table_path)s';
        $categories_list = %(categories_list)s;
        $max_retro = '%(max_retro)s';
        $min_retro = '%(min_retro)s';

        $week_lag = 7;
        $log_min_date = CAST(CAST($min_retro AS Date) - DateTime::IntervalFromDays(CAST($days_to_take + $week_lag AS Int16)) AS String);
        $log_max_date = CAST(CAST($max_retro AS Date) + DateTime::IntervalFromDays($week_lag) AS String);

        $input_data = (%(input_data)s);

        $find_categories = ($url)->{
            $func = ($where, $needle) -> {
                RETURN String::Contains($where, $needle);
            };

            $code = EvaluateCode(LambdaCode(($strCode)->{
                $members = ListMap($categories_list, ($x)->{
                    $atom = AtomCode($x);
                    $apply = FuncCode("Apply", QuoteCode($func), $strCode, ReprCode($x));
                    RETURN ListCode($atom, $apply);
                });
                RETURN FuncCode("AsStruct", $members);
            }));

            RETURN $code($url)
        };

        $categories2list = ($struct)->{
            $code = EvaluateCode(LambdaCode(($structCode)->{
                $members = ListMap($categories_list, ($x)->{
                    RETURN FuncCode("Member", $structCode, AtomCode($x));
                });
                RETURN FuncCode("AsList", $members);
            }));
            RETURN $code($struct)
        };

        $ts2date = ($ts)->{
            RETURN CAST(CAST($ts AS DateTime) AS Date)
        };

        INSERT INTO $features_table_path WITH TRUNCATE
        SELECT
            %(ext_id_key)s,
            $categories2list(MULTI_AGGREGATE_BY($find_categories(parent_url ?? referer), AGGREGATION_FACTORY("BOOL_OR"))) AS features
        FROM $input_data AS i
        INNER JOIN RANGE($contact_log_dir, $log_min_date, $log_max_date) AS l
        ON i.id_value = l.id_value_md5
        WHERE $ts2date(l.ts) BETWEEN $ts2date(i.`timestamp`) - DateTime::IntervalFromDays($days_to_take) AND $ts2date(i.`timestamp`)
        GROUP BY i.%(ext_id_key)s AS %(ext_id_key)s
        ORDER BY %(ext_id_key)s
    """)

    execute_yql(query=yql_query, yql_client=yql_client, params=dict(
        custom_pragmas=CLOUD_NODES_PRAGMAS if build_config.use_cloud_nodes else '',
        days_to_take=build_config.days_to_take,
        input_data="'{}'" .format(build_config.input_tables[0]) if build_config.is_retro else dedent("""
            SELECT {}, id_value, CAST(CAST(CAST('{}' AS Date) AS DateTime) AS Int64) AS `timestamp`
            FROM CONCAT({})
        """).format(
            build_config.ext_id_key,
            build_config.snapshot_date,
            ','.join(map("'{}'".format, build_config.input_tables)),
        ),
        features_table_path=build_config.features_table_path,
        contact_log_dir=build_config.contact_log_dir,
        categories_list=CATEGORIES_LIST,
        ext_id_key=build_config.ext_id_key,
        max_retro=build_config.max_retro,
        min_retro=build_config.min_retro,
    ), set_owners=False, syntax_version=1, title='YQL ' + build_config.tag)

    logger.info('Finish calculate contact actions features')
