from nile.api.v1 import (
    filters as nf,
    datetime as nd,
    Record
)

from .common import MEMORY_LIMIT, extract_some_user_id, cut_to_seconds, subsource_path_list

FEEDBACK_FREE_ID = 1000000000


def sprav_feedback_type(question_id, answer_id):
    if question_id == 'add_object':
        if answer_id == 'entrance':
            return 'poi_entrance'
        elif answer_id == 'organization':
            return 'poi'
    elif question_id in ('closed', 'opened'):
        return 'poi_status'
    elif question_id in ('entrance_problem', 'organization_entrance', 'wrong_entrance'):
        return 'poi_entrance'
    elif question_id == 'other' and answer_id == 'comment':
        return 'poi'

    # unknown combination
    return 'poi'


def construct_sprav_data(sprav_table, free_id):
    def sprav_mapper(rows):
        for row in rows:
            original_task = row.get('original_task')

            # FIXME: NMAPS-11837
            if original_task is None:
                continue

            point = original_task.get('form_point', {'lon': 0, 'lat': 0})

            status = row.get('status')
            if status in ('accepted', 'rejected'):
                resolution = status
            elif status == 'published':
                resolution = 'accepted'
            else:
                resolution = None

            # cut_to_seconds() fails on missing field (applied to None)
            created_at = cut_to_seconds(row.get('created_at')).replace('T', ' ')

            # encode() fails on missing field (applied to None)
            question_id = original_task.get('question_id').encode('utf-8')
            answer_id = original_task.get('answer_id').encode('utf-8')

            yandexuid = original_task.get('metadata', {}).get('yandexuid', '').encode('utf-8')

            form_id = original_task.get('form_id')
            object_id = original_task.get('object_id')

            add_organization = (form_id == 'organization' and question_id == 'add_object' and answer_id == 'organization')
            organization_id = (object_id if form_id == 'organization' else None)

            yield Record(
                id=free_id,
                task_id=row['id'],
                status=status,
                position=[float(point['lon']), float(point['lat'])],
                source='fbapi',
                type=sprav_feedback_type(question_id, answer_id),
                commit_ids=[],
                resolution=resolution,
                created_at=created_at,
                fbapi_created_date=nd.round_period(cut_to_seconds(created_at)),
                yandexuid=yandexuid,
                some_user_id=extract_some_user_id(original_task),
                subsource_path=subsource_path_list('sprav', original_task),
                add_organization=add_organization,
                organization_id=organization_id
            )

    return sprav_table.filter(
        nf.equals('service', 'sprav')
    ).map(sprav_mapper, enable_row_index=True)


def construct_sprav_history(sprav_changes_table):
    def history_reducer(groups):
        for key, records in groups:
            resolved = None
            published = None

            for record in records:
                modified_at = cut_to_seconds(record['created_at']).replace('T', ' ')

                status = record['status']
                if status in ('rejected', 'accepted'):
                    resolved = modified_at
                elif status == 'published':
                    if resolved is None:
                        resolved = modified_at
                    published = modified_at
                elif status == 'in_progress':
                    resolved = None
                    published = None

            yield Record(task_id=key['task_id'], resolved_at=resolved, deployed_at=published)

    changes = sprav_changes_table.project(
        'id', 'status', 'created_at', 'task_id'
    ).groupby('task_id').sort('created_at').reduce(
        history_reducer,

        memory_limit=MEMORY_LIMIT
    )

    return changes


def fix_missing_history(sprav_data):
    def fix_history(records):
        for record in records:
            resolved_at = record.get('resolved_at')
            deployed_at = record.get('deployed_at')
            if record['resolution'] is not None and resolved_at is None:
                resolved_at = record['created_at']

            if record['status'] == 'published' and deployed_at is None:
                deployed_at = record['created_at']

            yield Record(record, resolved_at=resolved_at, deployed_at=deployed_at)

    return sprav_data.map(
        fix_history,

        memory_limit=MEMORY_LIMIT
    )


def join_sprav(feedback_table, sprav_table, sprav_changes_table):
    sprav_data = sprav_table.call(
        construct_sprav_data,
        FEEDBACK_FREE_ID
    ).join(
        construct_sprav_history(sprav_changes_table),
        by='task_id',
        type='left',

        memory_limit=MEMORY_LIMIT,
    ).call(fix_missing_history).label('sprav_data')

    return feedback_table.concat(
        sprav_data
    )
