from maps.wikimap.stat.assessment.report.lib import util

from nile.api.v1 import extractors

import re


def _add_task_id(assessed_tasks):
    '''
    assessed_tasks:
    | action_by | action | graded_by | grade | geom | category_id |
    |-----------+--------+-----------+-------+------+-------------|
    | ...       | ...    | ...       | ...   | ...  | ...         |

    result:
    | action_by | action | graded_by | grade | geom | task_id                       |
    |-----------+--------+-----------+-------+------+-------------------------------|
    | ...       | ...    | ...       | ...   | ...  | moderation/action/category_id |
    '''
    def get_task_id(action, category_id):
        result = b'moderation/'

        if action == b'resolve':
            result += b'resolved/'
        elif action == b'close':
            result += b'closed/'
        else:
            raise ValueError(f'Unsupported action "{action}".')

        if category_id is None:
            category_id = b'common'

        return result + category_id

    return assessed_tasks.project(
        extractors.all(exclude=['category_id']),
        task_id=extractors.custom(get_task_id, 'action', 'category_id')
    )


def _get_paid_tasks(moderation_log):
    '''
    moderation_log:
    | puid | task_id                      | lat_min | lon_min | lat_max | lon_max | ... |
    |------+------------------------------+---------+---------+---------+---------+-----|
    | ...  | moderation/<action>/category | ...     | ...     | ...     | ...     |     |

    result:
    | lat                     | lon                     | task_id | action_by | action           |
    |-------------------------+-------------------------+---------+-----------+------------------|
    | (lat_min + lat_max) / 2 | (lon_min + lon_max) / 2 | ...     | puid      | task_id.<action> |
    '''
    def mean(a, b):
        return (a + b) / 2

    def extract_action(task_id):
        result = re.match(b'moderation/([a-z]+)/', task_id)
        if result is not None:
            if result.group(1) == b'resolved':
                return b'resolve'
            if result.group(1) == b'closed':
                return b'close'
        raise ValueError(f'Cannot extract action from task_id "{task_id}".')

    return moderation_log.project(
        lat=extractors.custom(mean, 'lat_min', 'lat_max'),
        lon=extractors.custom(mean, 'lon_min', 'lon_max'),
        task_id='task_id',
        action_by='puid',
        action=extractors.custom(extract_action, 'task_id')
    )


def get_tasks(job, commit_event_table, latest_grades, moderation_log):
    '''
    commit_event_table:
    | event_id | bounds_geom | primary_object_category_id | ... |
    |----------+-------------+----------------------------+-----|
    | ...      | ...         | ...                        | ... |

    latest_grades:
    | entity_id | action_by | action | graded_by | grade |
    |-----------+-----------+--------+-----------+-------|
    | ...       | ...       | ...    | ...       | ...   |

    moderation_log:
    | puid | task_id | lat_min | lon_min | lat_max | lon_max | ... |
    |------+---------+---------+---------+---------+---------+-----|
    | ...  | ...     | ...     | ...     | ...     | ...     |     |

    result:
    | lat | lon | task_id | action_by | action | graded_by | grade |
    |-----+-----+---------+-----------+--------+-----------+-------|
    | ... | ... | ...     | ...       | ...    | ...       | ...   |
    '''

    assessed_tasks = util.get_assessed_tasks(commit_event_table, latest_grades)
    assessed_tasks_with_task_id = _add_task_id(assessed_tasks)
    assessed_tasks_with_position = util.geom_to_lat_lon(assessed_tasks_with_task_id)

    paid_tasks = _get_paid_tasks(moderation_log)

    return util.concat_assessed_and_paid_tasks(job, assessed_tasks_with_position, paid_tasks)
