''' report_controllers
routes related to reports
'''
import datetime
from redis import Redis
from rq import use_connection
from rq_scheduler import Scheduler

from flask import (render_template, request, flash, redirect, url_for, session
    , abort, send_file)

from application import app

# view controls
from decorators import *
# mongoengine models
from models import *
# utility functions
import utilities
from tasks import delete_local_file


def _generate_report_items(report):
    ''' calculate stats and generate data for graphs ahead of report rendering
    return a dict of the calculations and objects
    '''
    # get all relevant data for the project
    conditions = {
        'project': report.project
        , 'unique': True
        , 'visible': True
    }
    entries = Entry.objects(**conditions)

    items = []
    for component in report.components:
        # determine if component is a stat or graph
        component_type = None
        try:
            component.statistic_type
            component_type = 'statistic'
        except:
            pass
        
        try:
            component.xaxis
            component_type = 'graph'
        except:
            pass

        if component_type == 'statistic':
            # find the statistic
            statistics = Statistic.objects(name=component.name
                , project=report.project)
            if not statistics:
                flash('Statistic "%s" not found' % component.name, 'error')
                return redirect(url_for('reports'
                    , org_label=report.project.organization.label
                    , project_label=report.project.label
                    , report_label=report.label))

            statistic = statistics[0]
            # apply relevant filters and then computes the statistic
            if statistic.pivot:
                # returns dict keyed by the statistic's pivot_values
                result = utilities.compute_pivot_statistic(statistic
                    , entries)
            else:
                # returns a single result
                result = utilities.compute_statistic(statistic, entries)

            # save values for rendering
            items.append({
                'type': 'statistic'
                , 'name': component.name
                , 'instance': statistic
                , 'result': result
            })

        elif component_type == 'graph':
            # find the graph
            graphs = Graph.objects(name=component.name
                , project=report.project)
            if not graphs:
                flash('Graph "%s" not found' % component.name, 'error')
                return redirect(url_for('reports'
                    , org_label=report.project.organization.label
                    , project_label=report.project.label
                    , report_label=report.label))

            graph = graphs[0]
            if graph.graph_type == 'line':
                if graph.xaxis and graph.yaxis:
                    data, count = utilities.generate_line_graph_data(graph)
                else:
                    data = []
                    flash('Define an x-axis and y-axis for plotting'
                        , 'error')
            
            elif graph.graph_type == 'pie':
                if graph.pie_header:
                    data, count = utilities.generate_pie_chart_data(graph)
                else:
                    flash('define a column to create this pie chart'
                            , 'warning')
            
            # save values for rendering
            items.append({
                'type': 'graph'
                , 'name': component.name
                , 'instance': graph
                , 'data': data
            })
    
    return items


rendering_route = '/organizations/<org_label>/projects/<project_label>/'
rendering_route += 'renderings/<rendering_id>'
@app.route(rendering_route)
@verification_required
def renderings(org_label, project_label, rendering_id):
    ''' deleting or downloading renderings
    /organizations/aquaya/projects/water-quality/renderings/4cmb1?delete=true
        : remove a rendering from the system and s3
    /organizations/aquaya/projects/water-quality/renderings/4cmb1?download=true
        : remove a rendering from the system and s3
    '''
    user = User.objects(email=session['email'])[0]
    
    orgs = Organization.objects(label=org_label)
    if not orgs:
        abort(404)
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)
    
    # find the project
    projects = Project.objects(label=project_label, organization=org) 
    if not projects:
        abort(404)
    
    # find the specified rendering
    renderings = Rendering.objects(id=rendering_id)
    if not renderings:
        abort(404)
    rendering = renderings[0]
    
    # save the report for later redirect
    report = rendering.report

    if request.args.get('delete', '') == 'true':
        # remove the rendering
        utilities.delete_rendering(rendering, user.email)

        flash('Rendering successfully deleted.', 'success')
        return redirect(url_for('reports'
            , org_label=report.project.organization.label
            , project_label=report.project.label, report_label=report.label))

    if request.args.get('download', '') == 'true':
        absolute_filename = utilities.download_rendering_from_s3(rendering)
        
        # delay the deletion so we have time to serve the file
        redis_config = app.config['REDIS_CONFIG']
        use_connection(Redis(redis_config['host'], redis_config['port']
                , password=redis_config['password']))
        scheduler = Scheduler()
        scheduler.enqueue_in(datetime.timedelta(seconds=60)
            , delete_local_file, absolute_filename)

        return send_file(absolute_filename, as_attachment=True)

    else:
        abort(404)


public_reports_route = '/organizations/<org_label>/projects/<project_label>/'
public_reports_route += 'public-reports'
@app.route(public_reports_route + '/<public_label>')
def public_reports(org_label, project_label, public_label):
    ''' public but unguessable route for viewing reports
    used only by wkhtmltopdf
    '''
    # find the report
    reports = Report.objects(public_label=public_label)
    if not reports:
        abort(404)
    report = reports[0]

    # calculate all statistics and graph datasets
    items = _generate_report_items(report)

    # get the current time
    current_time = datetime.datetime.utcnow()

    return render_template('report_preview.html', report=report, items=items
        , current_time=current_time)


report_route = '/organizations/<org_label>/projects/<project_label>/'
report_route += 'reports'
@app.route(report_route, defaults={'report_label': None})
@app.route(report_route + '/<report_label>', methods=['GET', 'POST'])
@verification_required
@csrf_protect
def reports(org_label, project_label, report_label):
    ''' creating reports
    /organizations/aquaya/projects/water-quality/reports
        : viewing a list of this project's reports
    /organizations/aquaya/projects/water-quality/reports?create=true
        : create a new report config, immediately redirect to editing
    /organizations/aquaya/projects/water-quality/reports/submitters
        : view a report
    /organizations/aquaya/projects/water-quality/reports/submitters?edit=true
        : edit a report; accepts GET or POST
    '''
    user = User.objects(email=session['email'])[0]
    
    orgs = Organization.objects(label=org_label)
    if not orgs:
        flash('Organization "%s" not found, sorry!' % org_label, 'warning')
        return redirect(url_for('organizations'))
    org = orgs[0]

    # permission-check
    if org not in user.organizations and not user.admin_rights:
        app.logger.error('%s tried to view a project but was \
            denied for want of admin rights' % session['email'])
        abort(404)
    
    # find the project
    projects = Project.objects(label=project_label, organization=org) 
    if not projects:
        flash('Project "%s" not found, sorry!' % project_label, 'warning')
        return redirect(url_for('organizations', org_label=org.label))
    project = projects[0]

    if request.method == 'POST':
        # we have a report_label
        reports = Report.objects(label=report_label, project=project)
        if not reports:
            abort(404)
        report = reports[0]

        form_type = request.form.get('form_type', '')
        if form_type == 'info':
            if report.name != request.form.get('name', ''):
                name = request.form.get('name', '')
                report.update(set__name = name)

                reports = Report.objects(project=project).only('label')
                labels = [r.label for r in reports]
                report.update(set__label = utilities.generate_label(name
                    , labels))

            report.update(set__description = request.form.get('description'
                , ''))

            report.reload()

            flash('Report details saved successfully.', 'success')
            return redirect(url_for('reports', org_label=org.label
                , project_label=project.label, report_label=report.label
                , edit='true'))
        
        elif form_type == 'components':
            # attach components to report
            # 'items' is of the form graph_id__4abcd00123 or statistic_id..
            # convert these to full objects
            items = request.form.getlist('items')
            components = []
            for item in items:
                prefix, item_id = item.split('__')
                item_type = prefix.split('_')[0]

                if item_type == 'graph':
                    graphs = Graph.objects(id=item_id)
                    if not graphs:
                        abort(404)
                    components.append(graphs[0])
                
                elif item_type == 'statistic':
                    statistics = Statistic.objects(id=item_id)
                    if not statistics:
                        abort(404)
                    components.append(statistics[0])
    
            report.update(set__components=components)

            flash('Report components saved successfully.', 'success')
            
            url = url_for('reports', org_label=org.label
                , project_label=project.label, report_label=report.label
                , edit='true')
            return redirect(url + '#components')
       

        elif form_type == 'admin':
            # delete the report
            name = report.name
            report.delete()
            app.logger.info('%s deleted report "%s"' % \
                (session['email'], name))
            flash('report "%s" was deleted successfully' % name, 'success')
            return redirect(url_for('reports', org_label=org.label
                , project_label=project.label))
        
        else:
            # bad 'form_type'
            abort(404)
       
        
    
    if request.method == 'GET':
        if report_label:
            reports = Report.objects(label=report_label, project=project)
            if not reports:
                app.logger.error('%s tried to access a report that does \
                    not exist' % session['email'])
                flash('Report "%s" not found, sorry!' % report_label
                    , 'warning')
                return redirect(url_for('projects', org_label=org.label
                    , project_label=project.label))
            report = reports[0]

            if request.args.get('edit', '') == 'true':
                # extract the component names for js typeahead
                available_graphs = Graph.objects(project=project)
                available_statistics = Statistic.objects(project=project)

                return render_template('report_edit.html'
                    , report=report
                    , available_graphs = available_graphs
                    , available_statistics = available_statistics)


            elif request.args.get('preview', '') == 'true':
                # create pdf preview of the report
                # calculate all statistics and graph datasets
                items = _generate_report_items(report)
                
                # get the current time
                current_time = datetime.datetime.utcnow()

                return render_template('report_preview.html', report=report
                    , items=items, current_time=current_time)

            
            elif request.args.get('render', '') == 'true':
                ''' create PDF versions of the preview
                also send them to s3
                '''

                # CSRF validation
                token = request.args.get('token', '')
                if not verify_token(token):
                    abort(403)

                utilities.create_rendering(report)
                
                flash('The report is now being rendered, this process may \
                    take a few moments.  Check the table below for \
                    updates', 'info')
                return redirect(url_for('reports', org_label=org.label
                    , project_label=project.label, report_label=report.label))

            else:
                # find all relevant renderings
                renderings = Rendering.objects(report=report).order_by(
                    '-creation_time')

                return render_template('report.html', report=report
                    , renderings=renderings)

        if request.args.get('create', '') == 'true':
            # create a new report

            # CSRF validation
            token = request.args.get('token', '')
            if not verify_token(token):
                abort(403)

            try:
                report_name = 'report-%s' \
                    % utilities.generate_random_string(6)
                new_report= Report(
                    creation_time = datetime.datetime.utcnow()
                    , creator = user
                    , label = report_name.lower()
                    , project = project
                    , name = report_name
                    , public_label = utilities.generate_random_string(24)
                )
                new_report.save() 
                app.logger.info('report created by %s' % session['email'])
                flash('Report created, please change the default values.'
                    , 'success')
            except:
                app.logger.error('report creation failed for %s' % \
                    session['email'])
                flash('There was an error, sorry :/', 'error')
                return redirect(url_for('projects', org_label=org.label
                    , project=project.label))
            
            # redirect to the editing screen
            return redirect(url_for('reports', org_label=org.label
                , project_label=project.label
                , report_label=new_report.label, edit='true'))
        
        # no statistic in particular was specified; show em all
        reports = Report.objects(project=project)

        return render_template('project_reports.html', project=project
            , reports=reports)
