# -*- coding: utf-8 -*-

# Import Python libs
from __future__ import absolute_import
import os
import os.path
import tempfile

# Import Salt Testing libs
from tests.support.unit import TestCase

# Import Salt libs
import salt.loader
import salt.config
import tests.integration as integration
from salt.exceptions import SaltRenderError
from salt.ext.six.moves import StringIO

# Import 3rd-party libs
from salt.ext import six


REQUISITES = ['require', 'require_in', 'use', 'use_in', 'watch', 'watch_in']


class StateConfigRendererTestCase(TestCase):

    def setUp(self):
        self.root_dir = tempfile.mkdtemp(dir=integration.TMP)
        self.state_tree_dir = os.path.join(self.root_dir, 'state_tree')
        self.cache_dir = os.path.join(self.root_dir, 'cachedir')
        if not os.path.isdir(self.root_dir):
            os.makedirs(self.root_dir)

        if not os.path.isdir(self.state_tree_dir):
            os.makedirs(self.state_tree_dir)

        if not os.path.isdir(self.cache_dir):
            os.makedirs(self.cache_dir)
        self.config = salt.config.minion_config(None)
        self.config['root_dir'] = self.root_dir
        self.config['state_events'] = False
        self.config['id'] = 'match'
        self.config['file_client'] = 'local'
        self.config['file_roots'] = dict(base=[self.state_tree_dir])
        self.config['cachedir'] = self.cache_dir
        self.config['test'] = False
        self._renderers = salt.loader.render(
            self.config,
            {'config.get': lambda a, b: False}
        )

    def tearDown(self):
        for attrname in ('config', '_renderers'):
            try:
                delattr(self, attrname)
            except AttributeError:
                continue

    def _render_sls(self,
                    content,
                    sls='',
                    saltenv='base',
                    argline='-G yaml . jinja',
                    **kws):
        return self._renderers['stateconf'](
            StringIO(content), saltenv=saltenv, sls=sls,
            argline=argline,
            renderers=salt.loader.render(self.config, {}),
            **kws
        )

    def test_state_config(self):
        result = self._render_sls('''
.sls_params:
  stateconf.set:
    - name1: value1
    - name2: value2

.extra:
  stateconf:
    - set
    - name: value

# --- end of state config ---

test:
  cmd.run:
    - name: echo name1={{sls_params.name1}} name2={{sls_params.name2}} {{extra.name}}
    - cwd: /
''', sls='test')
        self.assertEqual(len(result), 3)
        self.assertTrue('test::sls_params' in result and 'test' in result)
        self.assertTrue('test::extra' in result)
        self.assertEqual(result['test']['cmd.run'][0]['name'],
                         'echo name1=value1 name2=value2 value')

    def test_sls_dir(self):
        result = self._render_sls('''
test:
  cmd.run:
    - name: echo sls_dir={{sls_dir}}
    - cwd: /
''', sls='path.to.sls')
        self.assertEqual(
            result['test']['cmd.run'][0]['name'],
            'echo sls_dir=path{0}to'.format(os.sep))

    def test_states_declared_with_shorthand_no_args(self):
        result = self._render_sls('''
test:
  cmd.run:
    - name: echo testing
    - cwd: /
test1:
  pkg.installed
test2:
  user.present
''')
        self.assertEqual(len(result), 3)
        for args in (result['test1']['pkg.installed'],
                     result['test2']['user.present']):
            self.assertTrue(isinstance(args, list))
            self.assertEqual(len(args), 0)
        self.assertEqual(result['test']['cmd.run'][0]['name'], 'echo testing')

    def test_adding_state_name_arg_for_dot_state_id(self):
        result = self._render_sls('''
.test:
  pkg.installed:
    - cwd: /
.test2:
  pkg.installed:
    - name: vim
''', sls='test')
        self.assertEqual(
            result['test::test']['pkg.installed'][0]['name'], 'test'
        )
        self.assertEqual(
            result['test::test2']['pkg.installed'][0]['name'], 'vim'
        )

    def test_state_prefix(self):
        result = self._render_sls('''
.test:
  cmd.run:
    - name: echo renamed
    - cwd: /

state_id:
  cmd:
    - run
    - name: echo not renamed
    - cwd: /
''', sls='test')
        self.assertEqual(len(result), 2)
        self.assertTrue('test::test' in result)
        self.assertTrue('state_id' in result)

    def test_dot_state_id_in_requisites(self):
        for req in REQUISITES:
            result = self._render_sls('''
.test:
  cmd.run:
    - name: echo renamed
    - cwd: /

state_id:
  cmd.run:
    - name: echo not renamed
    - cwd: /
    - {0}:
      - cmd: .test

    '''.format(req), sls='test')
            self.assertEqual(len(result), 2)
            self.assertTrue('test::test' in result)
            self.assertTrue('state_id' in result)
            self.assertEqual(
                result['state_id']['cmd.run'][2][req][0]['cmd'], 'test::test'
            )

    def test_relative_include_with_requisites(self):
        for req in REQUISITES:
            result = self._render_sls('''
include:
  - some.helper
  - .utils

state_id:
  cmd.run:
    - name: echo test
    - cwd: /
    - {0}:
      - cmd: .utils::some_state
'''.format(req), sls='test.work')
            self.assertEqual(result['include'][1], {'base': 'test.utils'})
            self.assertEqual(
                result['state_id']['cmd.run'][2][req][0]['cmd'],
                'test.utils::some_state'
            )

    def test_relative_include_and_extend(self):
        result = self._render_sls('''
include:
  - some.helper
  - .utils

extend:
  .utils::some_state:
    cmd.run:
      - name: echo overridden
    ''', sls='test.work')
        self.assertTrue('test.utils::some_state' in result['extend'])

    def test_multilevel_relative_include_with_requisites(self):
        for req in REQUISITES:
            result = self._render_sls('''
include:
  - .shared
  - ..utils
  - ...helper

state_id:
  cmd.run:
    - name: echo test
    - cwd: /
    - {0}:
      - cmd: ..utils::some_state
'''.format(req), sls='test.nested.work')
            self.assertEqual(result['include'][0],
                             {'base': 'test.nested.shared'})
            self.assertEqual(result['include'][1], {'base': 'test.utils'})
            self.assertEqual(result['include'][2], {'base': 'helper'})
            self.assertEqual(
                result['state_id']['cmd.run'][2][req][0]['cmd'],
                'test.utils::some_state'
            )

    def test_multilevel_relative_include_beyond_top_level(self):
        self.assertRaises(SaltRenderError, self._render_sls, '''
include:
  - ...shared
''', sls='test.work')

    def test_start_state_generation(self):
        result = self._render_sls('''
A:
  cmd.run:
    - name: echo hello
    - cwd: /
B:
  cmd.run:
    - name: echo world
    - cwd: /
''', sls='test', argline='-so yaml . jinja')
        self.assertEqual(len(result), 4)
        self.assertEqual(
            result['test::start']['stateconf.set'][0]['require_in'][0]['cmd'],
            'A'
        )

    def test_goal_state_generation(self):
        result = self._render_sls('''
{% for sid in "ABCDE": %}
{{sid}}:
  cmd.run:
    - name: echo this is {{sid}}
    - cwd: /
{% endfor %}

''', sls='test.goalstate', argline='yaml . jinja')
        self.assertEqual(len(result), len('ABCDE') + 1)

        reqs = result['test.goalstate::goal']['stateconf.set'][0]['require']
        self.assertEqual(
            set([next(six.itervalues(i)) for i in reqs]), set('ABCDE')
        )

    def test_implicit_require_with_goal_state(self):
        result = self._render_sls('''
{% for sid in "ABCDE": %}
{{sid}}:
  cmd.run:
    - name: echo this is {{sid}}
    - cwd: /
{% endfor %}

F:
  cmd.run:
    - name: echo this is F
    - cwd: /
    - require:
      - cmd: A
      - cmd: B

G:
  cmd.run:
    - name: echo this is G
    - cwd: /
    - require:
      - cmd: D
      - cmd: F
''', sls='test', argline='-o yaml . jinja')

        sids = 'ABCDEFG'[::-1]
        for i, sid in enumerate(sids):
            if i < len(sids) - 1:
                self.assertEqual(
                    result[sid]['cmd.run'][2]['require'][0]['cmd'],
                    sids[i + 1]
                )

        F_args = result['F']['cmd.run']
        self.assertEqual(len(F_args), 3)
        F_req = F_args[2]['require']
        self.assertEqual(len(F_req), 3)
        self.assertEqual(F_req[1]['cmd'], 'A')
        self.assertEqual(F_req[2]['cmd'], 'B')

        G_args = result['G']['cmd.run']
        self.assertEqual(len(G_args), 3)
        G_req = G_args[2]['require']
        self.assertEqual(len(G_req), 3)
        self.assertEqual(G_req[1]['cmd'], 'D')
        self.assertEqual(G_req[2]['cmd'], 'F')

        goal_args = result['test::goal']['stateconf.set']
        self.assertEqual(len(goal_args), 1)
        self.assertEqual(
            [next(six.itervalues(i)) for i in goal_args[0]['require']],
            list('ABCDEFG')
        )

    def test_slsdir(self):
        result = self._render_sls('''
formula/woot.sls:
  cmd.run:
    - name: echo {{ slspath }}
    - cwd: /
''', sls='formula.woot', argline='yaml . jinja')

        r = result['formula/woot.sls']['cmd.run'][0]['name']
        self.assertEqual(r, 'echo formula/woot')
