"""Test Home Assistant json utility functions."""
from json import JSONEncoder
import os
import unittest
import sys
from tempfile import mkdtemp

from homeassistant.util.json import (
    SerializationError, load_json, save_json)
from homeassistant.exceptions import HomeAssistantError
import pytest

from unittest.mock import Mock

# Test data that can be saved as JSON
TEST_JSON_A = {"a": 1, "B": "two"}
TEST_JSON_B = {"a": "one", "B": 2}
# Test data that can not be saved as JSON (keys must be strings)
TEST_BAD_OBJECT = {("A",): 1}
# Test data that can not be loaded as JSON
TEST_BAD_SERIALIED = "THIS IS NOT JSON\n"


class TestJSON(unittest.TestCase):
    """Test util.json save and load."""

    def setUp(self):
        """Set up for tests."""
        self.tmp_dir = mkdtemp()

    def tearDown(self):
        """Clean up after tests."""
        for fname in os.listdir(self.tmp_dir):
            os.remove(os.path.join(self.tmp_dir, fname))
        os.rmdir(self.tmp_dir)

    def _path_for(self, leaf_name):
        return os.path.join(self.tmp_dir, leaf_name+".json")

    def test_save_and_load(self):
        """Test saving and loading back."""
        fname = self._path_for("test1")
        save_json(fname, TEST_JSON_A)
        data = load_json(fname)
        assert data == TEST_JSON_A

    # Skipped on Windows
    @unittest.skipIf(sys.platform.startswith('win'),
                     "private permissions not supported on Windows")
    def test_save_and_load_private(self):
        """Test we can load private files and that they are protected."""
        fname = self._path_for("test2")
        save_json(fname, TEST_JSON_A, private=True)
        data = load_json(fname)
        assert data == TEST_JSON_A
        stats = os.stat(fname)
        assert stats.st_mode & 0o77 == 0

    def test_overwrite_and_reload(self):
        """Test that we can overwrite an existing file and read back."""
        fname = self._path_for("test3")
        save_json(fname, TEST_JSON_A)
        save_json(fname, TEST_JSON_B)
        data = load_json(fname)
        assert data == TEST_JSON_B

    def test_save_bad_data(self):
        """Test error from trying to save unserialisable data."""
        fname = self._path_for("test4")
        with pytest.raises(SerializationError):
            save_json(fname, TEST_BAD_OBJECT)

    def test_load_bad_data(self):
        """Test error from trying to load unserialisable data."""
        fname = self._path_for("test5")
        with open(fname, "w") as fh:
            fh.write(TEST_BAD_SERIALIED)
        with pytest.raises(HomeAssistantError):
            load_json(fname)

    def test_custom_encoder(self):
        """Test serializing with a custom encoder."""
        class MockJSONEncoder(JSONEncoder):
            """Mock JSON encoder."""

            def default(self, o):
                """Mock JSON encode method."""
                return "9"

        fname = self._path_for("test6")
        save_json(fname, Mock(), encoder=MockJSONEncoder)
        data = load_json(fname)
        self.assertEqual(data, "9")