# w.c.s. - web application for online forms
# Copyright (C) 2005-2017  Entr'ouvert
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.

from __future__ import print_function

import json
import os

from django.core.management.base import CommandError

from quixote import get_publisher

from wcs.formdef import FormDef
from wcs.workflows import Workflow
from wcs.wf.jump import JumpWorkflowStatusItem, jump_and_perform as wcs_jump_and_perform

from . import TenantCommand


class Command(TenantCommand):
    '''Triggers all "jump trigger" for a formdef, given host publisher context

    source.json file format:
        [
            {
                "data": { "info_for_wf_data": 42, ... },
                "select": { "form_number": 1, ... }
            },
            ...
        ]
    '''

    def add_arguments(self, parser):
        parser.add_argument('-d', '--domain', metavar='DOMAIN', required=True)
        parser.add_argument('--trigger', metavar='TRIGGER', required=True)
        parser.add_argument('--workflow-id', metavar='WORKFLOW_ID')
        parser.add_argument('--formdef-id', metavar='FORMDEF_ID')
        parser.add_argument('--all-formdata', action='store_true')
        parser.add_argument('filenames', metavar='FILENAME', nargs='+')

    def handle(self, filenames, domain, trigger, workflow_id, formdef_id,
            all_formdata, **options):

        self.init_tenant_publisher(domain)

        if not all_formdata:
            rows = list(get_rows(filenames))
        else:
            rows = '__all__'

        if formdef_id:
            formdef = FormDef.get(id=formdef_id, ignore_errors=True)
            if not formdef:
                raise CommandError('formdef-id does not exist')
            select_and_jump_formdata(formdef, trigger, rows)
        else:
            if workflow_id:
                workflow = Workflow.get(id=workflow_id, ignore_errors=True)
                if not workflow:
                    raise CommandError('workflow does not exist')
                workflows = [workflow]
            else:
                workflows = Workflow.select()
            for workflow in workflows:
                status_ids = list(get_status_ids_accepting_trigger(workflow, trigger))
                if status_ids:
                    for formdef in [f for f in FormDef.select() if f.workflow_id == workflow.id]:
                        select_and_jump_formdata(formdef, trigger, rows, status_ids)


def get_rows(args):
    for arg in args:
        for row in json.load(file(arg)):
            yield row

def get_status_ids_accepting_trigger(workflow, trigger):
    for status in workflow.possible_status:
        for item in status.items:
            if isinstance(item, JumpWorkflowStatusItem) and \
                item.trigger == trigger:
                yield 'wf-%s' % status.id, item
                break

def get_formdata_accepting_trigger(formdef, trigger, status_ids=None):
    if status_ids is None:
        workflow = formdef.get_workflow()
        status_ids = get_status_ids_accepting_trigger(workflow, trigger)
    formdata_ids = []

    data_class = formdef.data_class()
    for status_id, action_item in status_ids:
        formdata_ids = data_class.get_ids_with_indexed_value('status', status_id)
        for formdata_id in formdata_ids:
            yield data_class.get(id=formdata_id), action_item

def match_row(substitution_variables, row):
    select = row['select']
    for key, value in select.items():
        if str(substitution_variables.get(key)) != str(value):
            return False
    return True

def jump_and_perform(formdata, action, workflow_data=None):
    get_publisher().substitutions.reset()
    get_publisher().substitutions.feed(get_publisher())
    get_publisher().substitutions.feed(formdata.formdef)
    get_publisher().substitutions.feed(formdata)
    print('formdata %s jumps to status %s' % (formdata, action.status))
    wcs_jump_and_perform(formdata, action, workflow_data=workflow_data)

def select_and_jump_formdata(formdef, trigger, rows, status_ids=None):
    for formdata, action_item in get_formdata_accepting_trigger(formdef, trigger, status_ids):
        if rows == '__all__':
            jump_and_perform(formdata, action=action_item)
        else:
            substitution_variables = formdata.get_substitution_variables()
            for row in rows:
                if match_row(substitution_variables, row):
                    jump_and_perform(formdata, action_item, row.get('data'))
                    break # next formdata
