from maps.wikimap.stat.libs.feedback import get_paid_operations_at_date, get_task_id

from yandex.maps.geolib3 import Point2, SpatialReference
from nile.api.v1 import extractors, Record

from binascii import hexlify


def _get_paid_operations_at_date(date, feedback, social_commit_event):
    '''
    feedback:
    | id  | position | source | type | history                  | commit_ids      | ... |
    |-----|----------+--------+------+--------------------------+-----------------+-----|
    | ... | ...      | ...    | ...  | [                        | [commitId, ...] | ... |
    |     |          |        |      |   {                      |                 |     |
    |     |          |        |      |     'operation':  bytes, |                 |     |
    |     |          |        |      |     'modifiedBy': int,   |                 |     |
    |     |          |        |      |     'modifiedAt': bytes, |                 |     |
    |     |          |        |      |     ...                  |                 |     |
    |     |          |        |      |   }                      |                 |     |
    |     |          |        |      | ]                        |                 |     |

    social_commit_event:
    | commit_id | created_at | type | ... |
    |-----------+------------+------+-----|
    | ...       | ...        | ...  | ... |

    result:
    | position | source | type          | operation         | puid               | iso_datetime       |
    |----------+--------+---------------+-------------------+--------------------+--------------------|
    | ...      | ...    | feedback.type | history.operation | history.modifiedBy | history.modifiedAt |
    '''
    paid_operations = get_paid_operations_at_date(date, feedback, social_commit_event)

    return paid_operations.project(
        extractors.all(exclude=['modified_by', 'modified_at']),
        puid='modified_by',
        iso_datetime='modified_at'
    )


def _point_to_ewkb(lon, lat):
    return hexlify(
        Point2(lon, lat).to_EWKB(SpatialReference.Epsg4326)
    )


def _position_to_geom_and_bbox(operations_at_date):
    '''
    Converts data from `position` column to `geom` (EWKB) and `lat/lon_min/max`
    columns.

    operations_at_date:
    | position | source | type | operation | puid | iso_datetime |
    |----------+--------+------+-----------+------+--------------|
    | ...      | ...    | ...  | ...       | ...  | ...          |

    Result:
    | source | type | operation | puid | iso_datetime | geom | lat_min | lon_min | lat_max | lon_max |
    |--------+------+-----------+------+--------------+------+---------+---------+---------+---------|
    | ...    | ...  | ...       | ...  | ...          | ...  | ...     | ...     | ...     | ...     |
    '''

    def position_to_bbox(records):
        for record in records:
            if 'position' not in record:
                continue

            assert len(record.position) == 2
            lon, lat = record.position

            yield Record(record, geom=_point_to_ewkb(lon, lat), lat_min=lat, lon_min=lon, lat_max=lat, lon_max=lon)

    return operations_at_date \
        .map(position_to_bbox) \
        .project(extractors.all(exclude=['position']))


def _get_task_id(operations_with_geom_and_bbox):
    '''
    Converts `operation`, `source` and `type` to `task_id`.

    operations_with_geom_and_bbox:
    | source | type | operation | puid | iso_datetime | geom | lat_min | lon_min | lat_max | lon_max |
    |--------+------+-----------+------+--------------+------+---------+---------+---------+---------|
    | ...    | ...  | ...       | ...  | ...          | ...  | ...     | ...     | ...     | ...     |

    Result:
    | task_id                              | puid | iso_datetime | geom | lat_min | lon_min | lat_max | lon_max |
    |--------------------------------------+------+--------------+------+---------+---------+---------+---------|
    | get_task_id(operation, source, type) | ...  | ...          | ...  | ...     | ...     | ...     | ...     |
    '''

    return operations_with_geom_and_bbox \
        .project(
            extractors.all(exclude=['source', 'type', 'operation']),
            task_id=extractors.custom(get_task_id, 'source', 'type', 'operation'),
        )


def _make_tasks_log(operations_with_task_id):
    '''
    Adds `quantity` column.

    operations_with_task_id:
    | task_id | puid | iso_datetime | geom | lat_min | lon_min | lat_max | lon_max |
    |---------+------+--------------+------+---------+---------+---------+---------|
    | ...     | ...  | ...          | ...  | ...     | ...     | ...     | ...     |

    Result:
    | iso_datetime | puid | task_id | quantity | geom | lat_min | lon_min | lat_max | lon_max |
    |--------------+------+---------+----------+------+---------+---------+---------+---------|
    |          ... |  ... |     ... |      1.0 |  ... |     ... |     ... |     ... |     ... |
    '''

    return operations_with_task_id \
        .project(
            extractors.all(),
            quantity=extractors.const(1.0)
        )


def make_feedback_log(date, feedback, social_commit_event):
    operations_at_date = _get_paid_operations_at_date(date, feedback, social_commit_event)
    operations_with_geom_and_bbox = _position_to_geom_and_bbox(operations_at_date)
    operations_with_task_id = _get_task_id(operations_with_geom_and_bbox)
    return _make_tasks_log(operations_with_task_id)
