from yt.wrapper import ypath_join
from nile.api.v1 import(
    extractors as ne,
    filters as nf
)
from qb2.api.v1 import (
    extractors as qe
)
from yandex.maps.geolib3 import Polygon2, SpatialReference
from maps.wikimap.stat.tasks_payment.tasks_logging.libs.schema import CARTOGRAPHIC_TASKS_LOG_SCHEMA
from maps.wikimap.stat.tasks_payment.tasks_logging.libs.parsing import isotime_normalizer
from maps.wikimap.stat.tasks_payment.dictionaries.libs.users import normalize_yalogin
from cyson import UInt
from binascii import hexlify


OUTSOURCE_REGIONS_PAYMENTS_TABLES = 'nmaps-outsource-regions-payments-log/1d'
OUTSOURCE_REGIONS_DUMP_TABLES = 'nmaps-outsource-regions-dump-log/1d'
USERS_DUMP_TABLES = 'nmaps-users-dump-log/1d'

RESULTS_TABLES = 'tasks_payment/tasks_logs/outsource_log'

COMPLEXITY_RATE_LABELS = {
    b'1.000000': b'1',
    b'1.500000': b'1.5',
    b'2.000000': b'2',
    b'3.000000': b'3'
}


def _task_id(task_type, complexity_rate):
    if complexity_rate not in COMPLEXITY_RATE_LABELS:
        raise ValueError('Unexpected complexity_rate value: {}'.format(complexity_rate.decode('utf8')))
    return b'/'.join([b'outsource', task_type, COMPLEXITY_RATE_LABELS[complexity_rate]])


def _quantity(work_amount):
    return float(work_amount.decode('utf8'))


def _unified_geometry_dict(geometry):
    polygon2 = Polygon2.from_WKT(geometry)
    bbox = polygon2.bounding_box()
    return {
        'lon_min': bbox.min_x,
        'lon_max': bbox.max_x,
        'lat_min': bbox.min_y,
        'lat_max': bbox.max_y,
        'geom': hexlify(polygon2.to_EWKB(SpatialReference.Epsg4326))
    }


def _outsource_regions_geometry(outsource_regions_dump):
    '''
    Projects [object_id, geometry] columns from outsource-regions-dump log

    nmaps-outsource-regions-dump-log:
    | object_id | geometry | ...
    +-----------+----------+-----
    |       ... |      ... | ...

    result:
    | object_id | geometry |
    +-----------+----------+
    |       ... |      ... |
    '''
    return outsource_regions_dump.project(
        'object_id',
        'geometry'
    )


def _accepted_outsource_regions(outsource_regions_payments):
    '''
    Projects columns from outsource-regions-payments.
    Filters out records with outsourcer_login="" due to
    https://st.yandex-team.ru/NMAPS-10819#5e43b3ef0be5a170c772eb07

    nmaps-outsource-regions-payments-log:
    | iso_eventtime | outsource_login | task_type | work_amount | complexity_rate | region_id | ... |
    |---------------+-----------------+-----------+-------------+-----------------+-----------+-----|
    | ...           | ...             | ...       | ...         | ...             | ...       | ... |

    result:
    | iso_datetime  | object_id | um_login                           | task_id                              | quantity    |
    |---------------+-----------+------------------------------------+--------------------------------------+-------------|
    | iso_eventtime | region_id | normalize_yalogin(outsource_login) | _task_id(task_type, complexity_rate) | work_amount |
    '''
    return outsource_regions_payments.filter(
        nf.custom(lambda login: len(login) > 0, 'outsourcer_login')
    ).project(
        iso_datetime=ne.custom(isotime_normalizer(), 'iso_eventtime'),
        object_id='region_id',  # id of outsource region
        um_login=ne.custom(normalize_yalogin, 'outsourcer_login'),
        task_id=ne.custom(_task_id, 'task_type', 'complexity_rate'),
        quantity=ne.custom(_quantity, 'work_amount')
    )


def _puid_um_login(users_dump):
    '''
    Projects [puid, um_login] columns from nmaps-users-dump log.
    WRN: nmaps-users-dump contains puid with um_login=""

    nmaps-users-dump-log:
    | puid | um_login | ... |
    |------+----------+-----|
    | ...  | ...      | ... |

    result:
    | puid | um_login                    |
    |------+-----------------------------|
    | ...  | normalize_yalogin(um_login) |
    '''
    return users_dump.project(
        puid=ne.custom(UInt, 'puid'),
        um_login=ne.custom(normalize_yalogin, 'um_login')
    )


def _unified_payments_log(outsource_geometry, accepted_outsource, nmaps_users):
    '''
    Joins info about accepted outsource regions.
    Geometry coords are assumed to be longitude and latitude.

    outsource_geometry:
    | object_id |       geometry |
    +-----------+----------------+
    |       ... |  <WKT POLYGON> |

    accepted_outsource:
    | iso_datetime | object_id | um_login | task_id | quantity |
    +--------------+-----------+----------+---------+----------+
    |          ... |       ... |      ... |     ... |      ... |

    nmaps_users:
    | puid | um_login |
    +------+----------+
    |  ... |      ... |

    result:
    | iso_datetime | puid | task_id | quantity | lon_min | lon_max | lat_min | lat_max | geom |
    +--------------+------+---------+----------+---------+---------+---------+---------+------+
    |          ... |  ... |     ... |      ... |     ... |     ... |     ... |     ... |  ... |
    '''
    return accepted_outsource.join(
        outsource_geometry,
        'object_id',
        type='left',
        assume_unique_right=True,
        # assume_small_left=True
    ).join(
        nmaps_users,
        'um_login',
        type='left',
        assume_unique_right=True,
        # assume_small_left=True
    ).project(
        'iso_datetime',
        'puid',
        'task_id',
        'quantity',
        qe.custom('unified_geometry_dict', _unified_geometry_dict, 'geometry').hide(),
        *[qe.dictitem(key, 'unified_geometry_dict') for key in ['lon_min', 'lon_max', 'lat_min', 'lat_max', 'geom']],
    )


def make_job(job, isodate, logs_path, results_path):
    outsource_regions_dump = job.table(
        ypath_join(logs_path, OUTSOURCE_REGIONS_DUMP_TABLES, isodate)
    ).label('nmaps-outsource-regions-dump-log')

    outsource_regions_payments = job.table(
        ypath_join(logs_path, OUTSOURCE_REGIONS_PAYMENTS_TABLES, isodate)
    ).label('nmaps-outsource-regions-payments-log')

    users_dump = job.table(
        ypath_join(logs_path, USERS_DUMP_TABLES, isodate)
    ).label('nmaps-users-dump-log')

    unified_payments = _unified_payments_log(
        _outsource_regions_geometry(outsource_regions_dump),
        _accepted_outsource_regions(outsource_regions_payments),
        _puid_um_login(users_dump)
    ).label('result')

    unified_payments.put(
        ypath_join(results_path, isodate),
        schema=CARTOGRAPHIC_TASKS_LOG_SCHEMA,
        ensure_optional=False
    )

    return job
