# -*- coding: utf-8 -*-

from sqlalchemy import create_engine
import yt.wrapper as yt
import argparse
import datetime
import logging
import os


YT_TABLE_SCHEMA = [
    {"name": "id",                "type": "uint64", "required": True},
    {"name": "position",          "type": "any"},
    {"name": "hidden",            "type": "boolean", "required": True},
    {"name": "object_id",         "type": "uint64"},
    {"name": "description",       "type": "any"},
    {"name": "source",            "type": "string", "required": True},
    {"name": "type",              "type": "string", "required": True},
    {"name": "workflow",          "type": "string", "required": True},
    {"name": "resolution",        "type": "string"},
    {"name": "reject_reason",     "type": "string"},
    {"name": "commit_ids",        "type": "any"},
    {"name": "attrs",             "type": "any"},
    {"name": "created_at",        "type": "string", "required": True},
    {"name": "resolved_at",       "type": "string"},
    {"name": "resolved_by",       "type": "int64"},
    {"name": "deployed_at",       "type": "string"},
    {"name": "duplicate_head_id", "type": "uint64"},
    {"name": "history",           "type": "any"}
]


logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s %(message)s'
)
logger = logging.getLogger("main-logger")


def main():

    parser = argparse.ArgumentParser(
        description="Use environment variables 'YT_TOKEN' and 'PGPASSWORD' to pass secrets."
    )
    parser.add_argument(
        '--yt-result-link',
        help='Result link path',
        required=True
    )
    parser.add_argument(
        '--yt-result-folder',
        help='Folder to write feedbacks table',
        required=True
    )
    parser.add_argument(
        '--db-connection-string',
        help="""Social database connection string,
        format: postgresql://user:@host.yandex.net:6432/mapspro""",
        required=True
    )
    parser.add_argument(
        '--store-time-minutes',
        help='How long we want to store old tables in minutes',
        required=True,
        type=int)

    args = parser.parse_args()

    query = """

    WITH feedback_commit_ids AS (
        -- As a result we want to have mapping: fb_id -> array[commit_id]
        -- even in case if array of commit_ids is empty, that's why
        -- we apply additional 'array_remove' to transform [null] to []
        --
        SELECT
            ft.id AS feedback_task_id,
            array_remove(array_agg(cft.commit_id), NULL) AS commit_ids
        FROM social.feedback_task ft
        LEFT JOIN social.commit_feedback_task cft
            ON ft.id = cft.feedback_task_id
        GROUP BY ft.id
    )
    SELECT
        ft.id,
        (ST_AsGeoJson(ST_Transform(position, 4326))::json)->'coordinates'
            AS position,
        ft.hidden,
        ft.object_id,
        ft.description,
        ft.source,
        ft.type,
        ft.workflow,
        ft.attrs,
        ft.resolution,
        ft.reject_reason,
        to_json(fci.commit_ids) AS commit_ids,
        ft.created_at::text,
        ft.resolved_at::text,
        ft.resolved_by,
        ft.deployed_at::text,
        ft.duplicate_head_id,
        (
            SELECT
                jsonb_agg(json_hist)
            FROM (
                SELECT
                    feedback_task_id
                    , json_build_object(
                        'operation', operation,
                        'modifiedBy', modified_by,
                        'modifiedAt', modified_at
                    ) AS json_hist
                FROM social.feedback_history
                WHERE feedback_task_id = id
                ORDER BY feedback_history_id
            ) AS ttt
            GROUP BY feedback_task_id
        ) AS history
    FROM social.feedback_task ft
    JOIN feedback_commit_ids fci
        ON ft.id = fci.feedback_task_id

    """

    logger.info("Executing social database query")

    db = create_engine(args.db_connection_string)
    result_set = db.execute(query)
    records = map(lambda row: dict(row.items()), result_set)

    logger.info("Result set rows: {}".format(result_set.rowcount))

    yt_client = yt.YtClient(
        "hahn",
        token=os.environ['YT_TOKEN']
    )

    now = datetime.datetime.now()
    utcnow = datetime.datetime.utcnow()

    table_path = os.path.join(
        args.yt_result_folder,
        "feedbacks_dump_" + now.strftime('%Y-%m-%dT%H:%M:%S.%fZ')
    )

    logger.info(
        "Writing data to YT path '{}'".format(table_path)
    )

    with yt.Transaction(client=yt_client):
        yt_client.create(
            "table",
            table_path,
            ignore_existing=True,
            recursive=True,
            attributes={"schema": YT_TABLE_SCHEMA}
        )

        yt_client.write_table(
            table_path,
            records
        )

        logger.info(
            "Creating link '{}'".format(args.yt_result_link)
        )

        yt_client.link(table_path, args.yt_result_link, force=True)

    for table in yt_client.search(args.yt_result_folder,
                                  node_type=["table"],
                                  attributes=["creation_time"]):
        exp_time = datetime.datetime.strptime(
            table.attributes.get('creation_time'),
            '%Y-%m-%dT%H:%M:%S.%fZ'
            ) + datetime.timedelta(minutes=args.store_time_minutes)

        if exp_time < utcnow:
            logger.info(
                "Dropping old table '{}'".format(table)
            )
            yt_client.remove(table)


if __name__ == '__main__':
    main()
