from nile.api.v1 import (
    filters as nf,
    aggregators as na,
    extractors as ne,
    datetime as nd,
    statface as ns,
    Record,
    cli
)

import datetime


def run_aggregation(tasks, export, task_type, start_date, end_date):

    def add_date_fields(records):
        for record in records:
            for date in nd.date_range(start_date, end_date, stringify=False):

                task_start = nd.Datetime.from_iso(record['task_start'])
                task_end = nd.Datetime.from_iso(record['task_end']) if 'task_end' in record else None

                sent = (date <= task_start < date.next())
                in_progress = task_start < date.next() and (task_end is None or task_end >= date.next())
                exported = task_end is not None and (date <= task_end < date.next())

                if exported:
                    state = record.get('state')
                    assert(state)
                    assert(state in (task_type, 'no-' + task_type, 'never-done'))
                exported_success = exported and state == task_type
                exported_fail = exported and state == 'no-' + task_type
                exported_never_done = exported and state == 'never-done'

                yield Record(
                    record,
                    fielddate=str(date.date()),
                    sent=sent,
                    in_progress=in_progress,
                    exported=exported,
                    exported_success=exported_success,
                    exported_fail=exported_fail,
                    exported_never_done=exported_never_done
                )

    aggregates = dict([
        (f, na.count(predicate=nf.equals(f, True)))
        for f in ('sent', 'in_progress', 'exported', 'exported_success', 'exported_fail', 'exported_never_done')
    ])

    export_deduplicated = export.project(
        'nmapsBuildingId', 'nmapsTaskId', 'createdAt', 'state',
        task_end=ne.custom(lambda ts: str(datetime.datetime.utcfromtimestamp(ts / 1000)), 'createdAt')
    ).groupby(
        'nmapsBuildingId', 'nmapsTaskId'
    ).top(1, by='createdAt', mode='max')

    daily_agg = tasks.project(
        ne.all(),
        task_start=ne.custom(lambda ts: str(datetime.datetime.utcfromtimestamp(ts)), 'timestamp')
    ).join(
        export_deduplicated,
        by_left=['buildingId', 'taskId'],
        by_right=['nmapsBuildingId', 'nmapsTaskId'],
        type='left'
    ).map(
        add_date_fields
    ).groupby('fielddate').aggregate(
        **aggregates
    ).label('daily_result')

    daily_agg_with_extrafields = daily_agg.project(
        ne.all(),
        week=ne.custom(nd.round_period_weekly, 'fielddate'),
        month=ne.custom(nd.round_period_monthly, 'fielddate')
    )

    period_aggregates = dict(
        [(f, na.sum(f)) for f in ('sent', 'exported', 'exported_success', 'exported_fail', 'exported_never_done')]
        + [('in_progress', na.last('in_progress', 'fielddate'))]
    )

    week_agg = daily_agg_with_extrafields.groupby('week').aggregate(
        **period_aggregates
    ).project(
        ne.all(exclude='week'),
        fielddate='week'
    ).label('weekly_result')

    month_agg = daily_agg_with_extrafields.groupby('month').aggregate(
        **period_aggregates
    ).project(
        ne.all(exclude='month'),
        fielddate='month'
    ).label('monthly_result')

    return {
        'daily': daily_agg,
        'weekly': week_agg,
        'monthly': month_agg
    }


def make_testable_job(job, task_type, start_date, end_date):
    tasks = job.table("").label('tasks')
    export = job.table("").label('export')

    run_aggregation(
        tasks,
        export,
        task_type,
        start_date,
        end_date
    )

    return job


def make_job(job, nirvana, options, statface_client):
    tasks_table, export_table = nirvana.input_tables

    tasks = job.table(tasks_table)
    export = job.table(export_table)

    start_date = datetime.date.today() - datetime.timedelta(days=180)
    end_date = datetime.date.today() - datetime.timedelta(days=1)

    aggregations = run_aggregation(
        tasks,
        export,
        options.task_type,
        start_date,
        end_date
    )

    for scale, stream in aggregations.iteritems():
        report = ns.StatfaceReport() \
            .path(options.report)\
            .scale(scale)\
            .client(statface_client)
        stream.publish(report, allow_change_job=True)

    return job


if __name__ == "__main__":
    make_job = cli.statinfra_job(
        options=[
            cli.Option('report', required=True),
            cli.Option('task_type', required=True, choices=['address', 'entry'])
        ]
    )(make_job)

    cli.run()
