create_table_import_events_tmp = """
    CREATE TEMPORARY TABLE import_events_tmp (
        id bigserial NOT NULL,
        biz_id int8 NOT NULL,
        lead_id int8 NULL,
        leads_ids int8[] NULL,
        lead_name varchar(256) NOT NULL,
        event_type_name varchar(256) NOT NULL,
        event_value varchar(64) NULL,
        events_amount int4 NOT NULL,
        event_timestamp timestamptz NOT NULL,
        data_source varchar(256) NOT NULL,
        source varchar(256) NOT NULL,
        passport_uid varchar(20) NULL,
        device_id varchar(36) NULL,
        yandex_uid varchar(32) NULL,
        CONSTRAINT ck_import_events_tmp_lead_events_events_amount_is_positive
            CHECK (events_amount > 0),
        CONSTRAINT ck_import_events_tmp_review_must_be_with_score CHECK (
            (
                event_value IS NOT NULL
                AND (event_type_name)::text = 'REVIEW'::text
            )
            OR (event_type_name)::text <> 'REVIEW'::text
        ),
        CONSTRAINT pk_import_events_tmp PRIMARY KEY (id)
    )
    ON COMMIT DROP;

    CREATE INDEX import_events_tmp_biz_id_btree_idx
        ON import_events_tmp USING btree (biz_id);
    CREATE INDEX import_events_tmp_lead_id_btree_idx
        ON import_events_tmp USING btree (lead_id);
    CREATE INDEX import_events_tmp_passport_uid_btree_idx
        ON import_events_tmp USING btree (passport_uid);
    CREATE INDEX import_events_tmp_device_id_btree_idx
        ON import_events_tmp USING btree (device_id);
    CREATE INDEX import_events_tmp_yandex_uid_btree_idx
        ON import_events_tmp USING btree (yandex_uid);
"""


analyze_import_events_tmp = "ANALYZE import_events_tmp"


set_events_passport_uids = """
    WITH closest_passport_uid AS (
        SELECT
            ie.id AS logged_out_id,
            (array_agg(ie2.passport_uid ORDER BY ie2.event_timestamp))[1]
                AS logged_in_passport_uid
        FROM import_events_tmp AS ie
        JOIN import_events_tmp AS ie2 USING (biz_id, device_id)
        WHERE
            ie.event_timestamp < ie2.event_timestamp
                AND ie.passport_uid IS NULL
                AND ie2.passport_uid IS NOT NULL
        GROUP BY ie.id
    )
    UPDATE import_events_tmp
    SET passport_uid = closest_passport_uid.logged_in_passport_uid
    FROM closest_passport_uid
    WHERE import_events_tmp.id = closest_passport_uid.logged_out_id
"""


set_events_lead_ids = """
    -- Events with passport_uid
    -- Match events by non-null passport_uid
    WITH matched AS (
        SELECT
            ie.id AS event_id,
            array_agg(leads.id ORDER BY leads.id DESC) AS leads_ids
        FROM import_events_tmp AS ie
        JOIN leads USING (biz_id, passport_uid)
        WHERE ie.passport_uid IS NOT NULL
        GROUP BY ie.id
    )
    UPDATE import_events_tmp
    SET leads_ids = matched.leads_ids
    FROM matched
    WHERE matched.event_id = import_events_tmp.id
    ;

    -- Match events by secondary params (device_id or yandex_uid)
    -- Note that we never merge events without passport_uid to leads with passport_uid
    WITH matched AS (
        SELECT
            ie.id AS event_id,
            array_agg(leads.id ORDER BY leads.id DESC) AS leads_ids
        FROM import_events_tmp AS ie
        JOIN leads
            ON (leads.passport_uid IS NULL)
                AND (ie.biz_id = leads.biz_id)
                AND (ie.device_id = leads.device_id OR ie.yandex_uid = leads.yandex_uid)
        WHERE ie.lead_id IS NULL
            AND (ie.device_id IS NOT NULL OR ie.yandex_uid IS NOT NULL)
        GROUP BY ie.id
    )
    UPDATE import_events_tmp
    SET leads_ids = import_events_tmp.leads_ids || matched.leads_ids
    FROM matched
    WHERE matched.event_id = import_events_tmp.id
    ;

    -- Match events with common passport_uid to common leads
    WITH
    passport_uid_leads AS (
        SELECT
            biz_id,
            passport_uid,
            unnest(leads_ids) AS li
        FROM import_events_tmp
        WHERE passport_uid IS NOT NULL AND leads_ids IS NOT NULL

    ),
    passport_uid_leads_agg AS (
        SELECT
            biz_id,
            passport_uid,
            array_agg(DISTINCT li) AS lis
        FROM passport_uid_leads
        GROUP BY biz_id, passport_uid
    )
    UPDATE import_events_tmp
    SET leads_ids = passport_uid_leads_agg.lis
    FROM passport_uid_leads_agg
    WHERE import_events_tmp.biz_id = passport_uid_leads_agg.biz_id
        AND import_events_tmp.passport_uid = passport_uid_leads_agg.passport_uid
    ;

    -- Set first matched lead as event lead and head of future merge
    UPDATE import_events_tmp
    SET lead_id = leads_ids[1]
    WHERE leads_ids IS NOT NULL
"""


merge_leads = """
    CREATE TEMPORARY TABLE leads_for_merge_tmp ON COMMIT DROP
    AS
    SELECT
        DISTINCT
        leads_ids[1] AS head_id,
        unnest(leads_ids[2:]) AS duplicate_id
    FROM import_events_tmp
    WHERE cardinality(leads_ids) > 1;

    CREATE INDEX leads_for_merge_tmp_head_id
        ON leads_for_merge_tmp USING btree(head_id);
    CREATE INDEX leads_for_merge_tmp_duplicate_id
        ON leads_for_merge_tmp USING btree(duplicate_id);
    ANALYZE leads_for_merge_tmp;

    -- Resolve chain duplicates
    -- One pass is enough because maximum cardinality of leads_ids is 2
    UPDATE leads_for_merge_tmp
    SET head_id = lfm2.head_id
    FROM leads_for_merge_tmp as lfm2
    WHERE leads_for_merge_tmp.head_id = lfm2.duplicate_id;

    -- Transfer all data from duplicate to head
    UPDATE lead_events
    SET lead_id = head_id
    FROM leads_for_merge_tmp
    WHERE lead_id = duplicate_id;

    UPDATE lead_revisions
    SET lead_id = head_id
    FROM leads_for_merge_tmp
    WHERE lead_id = duplicate_id;

    UPDATE import_events_tmp
    SET lead_id = head_id
    FROM leads_for_merge_tmp
    WHERE lead_id = duplicate_id;

    -- Update head lead source if duplicate was created earlier
    WITH leads_to_update_source AS (
        SELECT
            head_id,
            duplicate_lead.source AS duplicate_source
        FROM leads_for_merge_tmp
        LEFT JOIN leads AS head_lead
            ON leads_for_merge_tmp.head_id = head_lead.id
        LEFT JOIN leads AS duplicate_lead
            ON leads_for_merge_tmp.duplicate_id = duplicate_lead.id
        WHERE duplicate_lead.created_at < head_lead.created_at
    )
    UPDATE leads
    SET source = duplicate_source
    FROM leads_to_update_source
    WHERE id = head_id;

    -- Remove duplicate lead data
    DELETE FROM events_stat_precalced
    WHERE lead_id IN (SELECT duplicate_id FROM leads_for_merge_tmp);

    DELETE
    FROM leads
    WHERE id IN (SELECT duplicate_id FROM leads_for_merge_tmp);
"""


update_leads = """
    WITH leads_from_events AS (
        SELECT
            lead_id,
            last_not_null_value(passport_uid ORDER BY event_timestamp) AS passport_uid,
            last_not_null_value(device_id ORDER BY event_timestamp) AS device_id,
            last_not_null_value(yandex_uid ORDER BY event_timestamp) AS yandex_uid,
            last_not_null_value(lead_name ORDER BY event_timestamp) AS name
        FROM import_events_tmp
        WHERE lead_id IS NOT NULL
        GROUP BY lead_id
    ),
    updated_leads AS (
        UPDATE leads
        SET
            passport_uid = coalesce(leads.passport_uid, leads_from_events.passport_uid),
            device_id = coalesce(leads_from_events.device_id, leads.device_id),
            yandex_uid = coalesce(leads_from_events.yandex_uid, leads.yandex_uid),
            name = coalesce(leads_from_events.name, leads.name)
        FROM leads_from_events
        WHERE leads.id = leads_from_events.lead_id
            AND (
                -- Return only actually updated rows
                -- to not create fake lead revisions
                leads.passport_uid IS NULL
                AND leads_from_events.passport_uid IS NOT NULL
                OR
                leads_from_events.device_id IS NOT NULL
                AND leads.device_id IS DISTINCT FROM leads_from_events.device_id
                OR
                leads_from_events.yandex_uid IS NOT NULL
                AND leads.yandex_uid IS DISTINCT FROM leads_from_events.yandex_uid
                OR
                leads_from_events.name IS NOT NULL
                AND leads.name IS DISTINCT FROM leads_from_events.name
            )
        RETURNING leads.*
    )
    INSERT INTO lead_revisions (
        lead_id, biz_id, passport_uid,
        device_id, yandex_uid, data_source, name
    )
    SELECT id, biz_id, passport_uid, device_id, yandex_uid, 'YT'::lead_data_source, name
    FROM updated_leads
"""


create_leads = """
    WITH
    leads_to_create_by_passport_uid AS (
        SELECT
            biz_id,
            (array_agg(source ORDER BY event_timestamp))[1] AS source,
            passport_uid,
            last_not_null_value(device_id ORDER BY event_timestamp) AS device_id,
            last_not_null_value(yandex_uid ORDER BY event_timestamp) AS yandex_uid,
            last_not_null_value(lead_name ORDER BY event_timestamp) AS name
        FROM import_events_tmp
        WHERE lead_id IS NULL AND passport_uid IS NOT NULL
        GROUP BY biz_id, passport_uid
    ),
    leads_to_create_by_device_id AS (
        SELECT
            biz_id,
            (array_agg(source ORDER BY event_timestamp))[1] AS source,
            NULL AS passport_uid,
            device_id,
            NULL AS yandex_uid,
            last_not_null_value(lead_name ORDER BY event_timestamp) AS name
        FROM import_events_tmp
        WHERE lead_id IS NULL AND passport_uid IS NULL AND device_id IS NOT NULL
        GROUP BY biz_id, device_id
    ),
    leads_to_create_by_yandex_uid AS (
        SELECT
            biz_id,
            (array_agg(source ORDER BY event_timestamp))[1] AS source,
            NULL AS passport_uid,
            NULL AS device_id,
            yandex_uid,
            last_not_null_value(lead_name ORDER BY event_timestamp) AS name
        FROM import_events_tmp
        WHERE lead_id IS NULL
            AND passport_uid IS NULL
            AND device_id IS NULL
            AND yandex_uid IS NOT NULL
        GROUP BY biz_id, yandex_uid
    ),
    leads_to_create AS (
        SELECT * FROM leads_to_create_by_passport_uid
        UNION ALL
        SELECT * FROM leads_to_create_by_device_id
        UNION ALL
        SELECT * FROM leads_to_create_by_yandex_uid
    ),
    created_leads AS (
        INSERT INTO leads (
            biz_id,
            passport_uid,
            device_id,
            yandex_uid,
            data_source,
            name,
            source
        )
        SELECT
            biz_id,
            passport_uid,
            device_id,
            yandex_uid,
            'YT'::lead_data_source,
            name,
            source::source
        FROM leads_to_create
        RETURNING *
    ),
    inserted_lead_revisions AS (
        INSERT INTO lead_revisions (
            lead_id,
            biz_id,
            passport_uid,
            device_id,
            yandex_uid,
            data_source,
            name
        )
        SELECT id, biz_id, passport_uid, device_id, yandex_uid, data_source, name
        FROM created_leads
    ),
    set_lead_id_by_passport_uid AS (
        UPDATE import_events_tmp AS ie
        SET lead_id = created_leads.id
        FROM created_leads
        WHERE created_leads.passport_uid IS NOT NULL
            AND ie.biz_id = created_leads.biz_id
            AND ie.passport_uid = created_leads.passport_uid
    ),
    set_lead_id_by_device_id AS (
        UPDATE import_events_tmp AS ie
        SET lead_id = created_leads.id
        FROM created_leads
        WHERE created_leads.passport_uid IS NULL
            AND created_leads.device_id IS NOT NULL
            AND ie.biz_id = created_leads.biz_id
            AND ie.device_id = created_leads.device_id
    ),
    set_lead_id_by_yandex_uid AS (
        UPDATE import_events_tmp AS ie
        SET lead_id = created_leads.id
        FROM created_leads
        WHERE created_leads.passport_uid IS NULL
            AND created_leads.device_id IS NULL
            AND created_leads.yandex_uid IS NOT NULL
            AND ie.biz_id = created_leads.biz_id
            AND ie.yandex_uid = created_leads.yandex_uid
    )
    SELECT
"""


insert_lead_events = """
    INSERT INTO lead_events (
        lead_id,
        biz_id,
        event_type,
        event_value,
        events_amount,
        event_timestamp,
        data_source,
        source
    )
    SELECT
        lead_id,
        biz_id,
        event_type_name::event_type,
        event_value,
        events_amount,
        event_timestamp,
        data_source::event_data_source,
        source::source
    FROM import_events_tmp
"""


refresh_events_stat_precalced = """
    CREATE TEMPORARY TABLE affected_leads (
        lead_id bigint NOT NULL UNIQUE
    )
    ON COMMIT DROP;
    
    INSERT INTO affected_leads
    SELECT DISTINCT lead_id AS lead_id
    FROM import_events_tmp;

    DELETE FROM events_stat_precalced
    WHERE lead_id IN (SELECT lead_id FROM affected_leads);

    
    WITH lead_events_sumed AS (
        SELECT
            lead_id,
            event_timestamp AS ts,
            sum(events_amount) 
                OVER (PARTITION BY lead_id ORDER BY event_timestamp DESC)
                AS events_amount_sum
        FROM lead_events
        WHERE lead_id IN (SELECT lead_id FROM affected_leads)
    ),
    lead_last_3_events_ts AS (
        SELECT
            lead_id,
            max(ts) AS last_3_events_timestamp
        FROM lead_events_sumed
        WHERE events_amount_sum >= 3
        GROUP BY lead_id
    )
    INSERT INTO events_stat_precalced (
        biz_id,
        lead_id,
        total_clicks_on_phone,
        total_site_opens,
        total_make_routes,
        total_view_working_hours,
        total_view_entrances,
        total_cta_button_click,
        total_favourite_click,
        total_location_sharing,
        total_booking_section_interaction,
        total_showcase_product_click,
        total_promo_to_site,
        total_geoproduct_button_click,
        last_review_rating,
        last_activity_timestamp,
        last_3_events_timestamp
    )
    SELECT
        biz_id,
        lead_id,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='CLICK_ON_PHONE'),0
        ) AS total_clicks_on_phone,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='OPEN_SITE'), 0
        ) AS total_site_opens,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='MAKE_ROUTE'), 0
        ) AS total_make_routes,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='VIEW_WORKING_HOURS'), 0
        ) AS total_view_working_hours,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='VIEW_ENTRANCES'), 0
        ) AS total_view_entrances,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='CTA_BUTTON_CLICK'), 0
        ) AS total_cta_button_click,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='FAVOURITE_CLICK'), 0
        ) AS total_favourite_click,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='LOCATION_SHARING'), 0
        ) AS total_location_sharing,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='BOOKING_SECTION_INTERACTION'), 0
        ) AS total_booking_section_interaction,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='SHOWCASE_PRODUCT_CLICK'), 0
        ) AS total_showcase_product_click,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='PROMO_TO_SITE'), 0
        ) AS total_promo_to_site,
        COALESCE(
            SUM(events_amount) FILTER (WHERE event_type='GEOPRODUCT_BUTTON_CLICK'), 0
        ) AS total_geoproduct_button_click,
        (
            array_agg(event_value ORDER BY event_timestamp DESC NULLS LAST)
            FILTER (WHERE event_type = 'REVIEW')
        )[1] AS last_review_rating,
        max(event_timestamp) AS last_activity_timestamp,
        max(lead_last_3_events_ts.last_3_events_timestamp) AS last_3_events_timestamp
    FROM lead_events
    LEFT JOIN lead_last_3_events_ts USING (lead_id)
    WHERE lead_events.lead_id IN (SELECT lead_id FROM affected_leads)
    GROUP BY biz_id, lead_id
"""  # noqa
