# Copyright 2014 - Mirantis, Inc.
# Copyright 2015 - StackStorm, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
#    you may not use this file except in compliance with the License.
#    You may obtain a copy of the License at
#
#        http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing, software
#    distributed under the License is distributed on an "AS IS" BASIS,
#    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#    See the License for the specific language governing permissions and
#    limitations under the License.

from mistral.lang import types
from mistral.lang.v2 import base
from mistral.lang.v2 import publish

NEXT_TASK = {
    "oneOf": [
        {
            "type": "string",
            "pattern": r"^\S+$",
            "description": "Task name (e.g.: `task1`)"
        },
        {
            "type": "string",
            "pattern": r"^\w+ \w+=(.*)$",
            "description": "Task name with dict parameter "
                           "(e.g.: `fail msg=\"test\"`, "
                           "`fail msg=<% task() %>`)"
        },
        {
            "type": "string",
            "pattern": r"^\w+\(\w+=(.*)\)$",
            "description": "Task name with func parameter "
                           "(e.g.: `fail(msg=\"test\")`, "
                           "`fail(msg=<% task() %>)`)"
        }
    ]
}

TASK_WITH_EXPRESSION = {
    "type": "object",
    "minProperties": 1,
    "maxProperties": 1,
    "patternProperties": {
        x['pattern']: types.EXPRESSION for x in NEXT_TASK['oneOf']
    },
    "description": "All next task variants plus expression (e.g.: "
                   "`task1: <% $.vm_id != null %>`, "
                   "`fail(msg=\"test\"): <% $.vm_id != null %>`)."
}

LIST_OF_TASKS = {
    "type": "array",
    "items": {
        "oneOf": [
            NEXT_TASK,
            TASK_WITH_EXPRESSION
        ]
    },
    "uniqueItems": True,
    "minItems": 1
}

ADVANCED_PUBLISHING_DICT = {
    "type": "object",
    "minProperties": 1,
    "properties": {
        "publish": publish.PublishSpec.get_schema(),
        "next": {
            "oneOf": [
                NEXT_TASK,
                TASK_WITH_EXPRESSION,
                LIST_OF_TASKS
            ]
        }
    },
    "additionalProperties": False
}


class OnClauseSpec(base.BaseSpec):
    _schema = {
        "oneOf": [
            NEXT_TASK,
            TASK_WITH_EXPRESSION,
            LIST_OF_TASKS,
            ADVANCED_PUBLISHING_DICT
        ]
    }

    def __init__(self, data, validate):
        super(OnClauseSpec, self).__init__(data, validate)

        if not isinstance(data, dict):
            # Old simple schema.
            self._publish = None
            self._next = prepare_next_clause(data)
        else:
            # New advanced schema.
            self._publish = self._spec_property('publish', publish.PublishSpec)
            self._next = prepare_next_clause(data.get('next'))

    @classmethod
    def get_schema(cls, includes=('definitions',)):
        return super(OnClauseSpec, cls).get_schema(includes)

    def get_publish(self):
        return self._publish

    def get_next(self):
        return self._next


def _as_list_of_tuples(data):
    if not data:
        return []

    if isinstance(data, str):
        return [_as_tuple(data)]

    return [_as_tuple(item) for item in data]


def _as_tuple(val):
    return list(val.items())[0] if isinstance(val, dict) else (val, '')


def prepare_next_clause(next_clause):
    list_of_tuples = _as_list_of_tuples(next_clause)

    for i, task in enumerate(list_of_tuples):
        task_name, params = OnClauseSpec._parse_cmd_and_input(task[0])

        list_of_tuples[i] = (task_name, task[1], params)

    return list_of_tuples
