from nile.api.v1 import extractors, filters

from yandex.maps.geolib3 import mercator_to_geo, Point2, Polygon2

from cyson import UInt

import logging


_ATLANTIS_COORDS = Point2(0.0, 0.0)


def extract_as_uint(column_name):
    return extractors.custom(lambda value: UInt(value), column_name)


def filter_by_date(column_name, date):
    date = date.encode()
    return filters.custom(
        lambda value: value is not None and value.startswith(date),
        column_name
    )


def _geom_to_center_point_lat_lon(hex_encoded_mercator_wkb):
    center = _ATLANTIS_COORDS

    if hex_encoded_mercator_wkb is not None:
        try:
            mercator_geom = Polygon2.from_EWKB(bytes.fromhex(hex_encoded_mercator_wkb.decode()))
            center = mercator_to_geo(mercator_geom.bounding_box().center())
        except:
            logging.warning("Wrong EWKB: '%s'.", hex_encoded_mercator_wkb.decode())

    return center.lat, center.lon


def geom_to_lat_lon(table):
    '''
    | geom | ... |
    |------+-----|
    | ...  | ... |

    result:
    | lat | lon | ... |
    |-----+-----+-----|
    | ... | ... | ... |
    '''

    def geom_to_lat_lon(records):
        for record in records:
            lat, lon = _geom_to_center_point_lat_lon(record.geom)
            yield record.transform('geom', lat=lat, lon=lon)

    return table.map(geom_to_lat_lon)


def get_assessed_tasks(commit_event_table, latest_grades):
    '''
    commit_event_table:
    | event_id (int) | bounds_geom | primary_object_category_id | ... |
    |----------------+-------------+----------------------------+-----|
    | ...            | ...         | ...                        | ... |

    latest_grades:
    | entity_id (string) | action_by | action | graded_by | grade |
    |--------------------+-----------+--------+-----------+-------|
    | ...                | ...       | ...    | ...       | ...   |

    result:
    | action_by | action | graded_by | grade | geom        | category_id                |
    |-----------+--------+-----------+-------+-------------+----------------------------|
    | ...       | ...    | ...       | ...   | bounds_geom | primary_object_category_id |
    '''
    latest_grades = latest_grades.project(
        extractors.all(),
        entity_id=extract_as_uint('entity_id')
    )

    return commit_event_table.project(
        entity_id=extract_as_uint('event_id'),
        geom='bounds_geom',
        category_id='primary_object_category_id'
    ).join(
        latest_grades,
        type='inner',
        by='entity_id',
    ).project(
        extractors.all(exclude=['entity_id'])
    )


def concat_assessed_and_paid_tasks(job, assessed_tasks, paid_tasks):
    '''
    assessed_tasks:
    | graded_by | grade | ... |
    |-----------+-------+-----|
    | ...       | ...   | ... |

    paid_tasks:
    | ... |
    |-----|
    | ... |

    result:
    | graded_by | grade | ... |
    |-----------+-------+-----|
    | ...       | ...   | ... |

    `graded_by` and `grade` are equal to None for entries from paid_tasks.
    '''

    return job.concat(
        assessed_tasks,
        paid_tasks.project(
            extractors.all(),
            graded_by=extractors.const(None),
            grade=extractors.const(None)
        )
    )
