# -*- coding: utf-8 -*-
'''
Tests for salt.utils.json
'''
# Import Python libs
from __future__ import absolute_import, print_function, unicode_literals
import textwrap

# Import Salt Testing libs
from tests.support.helpers import with_tempfile
from tests.support.mock import patch, MagicMock, NO_MOCK, NO_MOCK_REASON
from tests.support.unit import TestCase, LOREM_IPSUM, skipIf

# Import Salt libs
import salt.utils.files
import salt.utils.json
import salt.utils.platform
import salt.utils.stringutils
from salt.ext import six


class JSONTestCase(TestCase):
    data = {
        'спам': 'яйца',
        'list': [1, 2, 'three'],
        'dict': {'subdict': {'спам': 'яйца'}},
        'True': False,
        'float': 1.5,
        'None': None,
    }

    serialized = salt.utils.stringutils.to_str(
        '{"None": null, "True": false, "dict": {"subdict": {"спам": "яйца"}}, "float": 1.5, "list": [1, 2, "three"], "спам": "яйца"}'
    )

    serialized_indent4 = salt.utils.stringutils.to_str(textwrap.dedent('''\
        {
            "None": null,
            "True": false,
            "dict": {
                "subdict": {
                    "спам": "яйца"
                }
            },
            "float": 1.5,
            "list": [
                1,
                2,
                "three"
            ],
            "спам": "яйца"
        }'''))

    def test_find_json(self):
        test_sample_json = '''
                            {
                                "glossary": {
                                    "title": "example glossary",
                                    "GlossDiv": {
                                        "title": "S",
                                        "GlossList": {
                                            "GlossEntry": {
                                                "ID": "SGML",
                                                "SortAs": "SGML",
                                                "GlossTerm": "Standard Generalized Markup Language",
                                                "Acronym": "SGML",
                                                "Abbrev": "ISO 8879:1986",
                                                "GlossDef": {
                                                    "para": "A meta-markup language, used to create markup languages such as DocBook.",
                                                    "GlossSeeAlso": ["GML", "XML"]
                                                },
                                                "GlossSee": "markup"
                                            }
                                        }
                                    }
                                }
                            }
                            '''
        expected_ret = {'glossary': {'GlossDiv': {'GlossList': {'GlossEntry': {
            'GlossDef': {'GlossSeeAlso': ['GML', 'XML'],
                         'para': 'A meta-markup language, used to create markup languages such as DocBook.'},
            'GlossSee': 'markup', 'Acronym': 'SGML', 'GlossTerm': 'Standard Generalized Markup Language',
            'SortAs': 'SGML',
            'Abbrev': 'ISO 8879:1986', 'ID': 'SGML'}}, 'title': 'S'}, 'title': 'example glossary'}}

        # First test the valid JSON
        ret = salt.utils.json.find_json(test_sample_json)
        self.assertDictEqual(ret, expected_ret)

        # Now pre-pend some garbage and re-test
        garbage_prepend_json = '{0}{1}'.format(LOREM_IPSUM, test_sample_json)
        ret = salt.utils.json.find_json(garbage_prepend_json)
        self.assertDictEqual(ret, expected_ret)

        # Test to see if a ValueError is raised if no JSON is passed in
        self.assertRaises(ValueError, salt.utils.json.find_json, LOREM_IPSUM)

    @skipIf(salt.utils.platform.is_windows(), 'skip until we figure out what to do about decoding unicode on windows')
    @skipIf(not six.PY2, 'Test only needed on Python 2')
    @skipIf(NO_MOCK, NO_MOCK_REASON)
    def test_find_json_unicode_splitlines(self):
        '''
        Tests a case in salt-ssh where a unicode string is split into a list of
        str types by .splitlines().
        '''
        raw = '{"foo": "öäü"}'
        mock_split = MagicMock(return_value=[raw.encode('utf8')])

        with patch.object(salt.utils.json, '__split', mock_split):
            ret = salt.utils.json.find_json(raw)
            self.assertEqual(ret, {'foo': 'öäü'})

    def test_dumps_loads(self):
        '''
        Test dumping to and loading from a string
        '''
        # Dump with no indentation
        ret = salt.utils.json.dumps(self.data, sort_keys=True)
        # Make sure the result is as expected
        self.assertEqual(ret, self.serialized)
        # Loading it should be equal to the original data
        self.assertEqual(salt.utils.json.loads(ret), self.data)

        # Dump with 4 spaces of indentation
        ret = salt.utils.json.dumps(self.data, sort_keys=True, indent=4)
        # Make sure the result is as expected. Note that in Python 2, dumping
        # results in trailing whitespace on lines ending in a comma. So, for a
        # proper comparison, we will have to run rstrip on each line of the
        # return and then stitch it back together.
        ret = str('\n').join([x.rstrip() for x in ret.splitlines()])  # future lint: disable=blacklisted-function
        self.assertEqual(ret, self.serialized_indent4)
        # Loading it should be equal to the original data
        self.assertEqual(salt.utils.json.loads(ret), self.data)

    @with_tempfile()
    def test_dump_load(self, json_out):
        '''
        Test dumping to and loading from a file handle
        '''
        with salt.utils.files.fopen(json_out, 'wb') as fp_:
            fp_.write(salt.utils.stringutils.to_bytes(salt.utils.json.dumps(self.data)))
        with salt.utils.files.fopen(json_out, 'rb') as fp_:
            ret = salt.utils.json.loads(salt.utils.stringutils.to_unicode(fp_.read()))
            # Loading should be equal to the original data
            self.assertEqual(ret, self.data)
