# 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 on_clause
from mistral.lang.v2 import policies
from mistral.lang.v2 import retry_policy


# TODO(rakhmerov): This specification should be broken into two separate
# specs for direct and reverse workflows. It's weird to combine them into
# one because they address different use cases.


class TaskDefaultsSpec(base.BaseSpec):
    # See http://json-schema.org
    _schema = {
        "type": "object",
        "properties": {
            "retry": retry_policy.RetrySpec.get_schema(),
            "wait-before": types.EXPRESSION_OR_POSITIVE_INTEGER,
            "wait-after": types.EXPRESSION_OR_POSITIVE_INTEGER,
            "timeout": types.EXPRESSION_OR_POSITIVE_INTEGER,
            "pause-before": types.EXPRESSION_OR_BOOLEAN,
            "concurrency": types.EXPRESSION_OR_POSITIVE_INTEGER,
            "fail-on": types.EXPRESSION_OR_BOOLEAN,
            "on-complete": on_clause.OnClauseSpec.get_schema(),
            "on-success": on_clause.OnClauseSpec.get_schema(),
            "on-error": on_clause.OnClauseSpec.get_schema(),
            "safe-rerun": types.EXPRESSION_OR_BOOLEAN,
            "requires": {
                "oneOf": [types.NONEMPTY_STRING, types.UNIQUE_STRING_LIST]
            }
        },
        "additionalProperties": False
    }

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

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

        self._policies = self._group_spec(
            policies.PoliciesSpec,
            'retry',
            'wait-before',
            'wait-after',
            'timeout',
            'pause-before',
            'concurrency',
            'fail-on'
        )

        on_spec_cls = on_clause.OnClauseSpec

        self._on_complete = self._spec_property('on-complete', on_spec_cls)
        self._on_success = self._spec_property('on-success', on_spec_cls)
        self._on_error = self._spec_property('on-error', on_spec_cls)

        self._safe_rerun = data.get('safe-rerun')

        # TODO(rakhmerov): 'requires' should reside in a different spec for
        # reverse workflows.
        self._requires = data.get('requires', [])

    def validate_schema(self):
        super(TaskDefaultsSpec, self).validate_schema()

        self.validate_expr(self._data.get('safe-rerun', {}))

    def validate_semantics(self):
        # Validate YAQL expressions.
        self._validate_transitions(self._on_complete)
        self._validate_transitions(self._on_success)
        self._validate_transitions(self._on_error)

    def _validate_transitions(self, on_clause_spec):
        val = on_clause_spec.get_next() if on_clause_spec else []

        if not val:
            return

        [self.validate_expr(t)
            for t in ([val] if isinstance(val, str) else val)]

    def get_policies(self):
        return self._policies

    def get_on_complete(self):
        return self._on_complete

    def get_on_success(self):
        return self._on_success

    def get_on_error(self):
        return self._on_error

    def get_safe_rerun(self):
        return self._safe_rerun

    def get_requires(self):
        if isinstance(self._requires, str):
            return [self._requires]

        return self._requires
