# -*- coding: utf-8 -*-
import codecs
import concurrent.futures
import json
import os
import tempfile
from unittest import mock

from homehub_backend.lib import state_store


class Test_StateStore:
    def setup_method(self):
        fh, self.state_path = tempfile.mkstemp()
        os.close(fh)

    def teardown_method(self):
        os.remove(self.state_path)

    def test_init(self):
        store = state_store.StateStore(self.state_path)
        assert store.state == {'backend': {}, 'frontend': {}}
        assert store.state_path == self.state_path
        assert store.threadpool is None

    def test_load_state(self):
        # Given
        saved_state = {
            'backend': {
                'spam': True,
            },
            'frontend': {
                'eggs': True,
            },
        }

        with codecs.open(self.state_path, 'w', 'utf-8') as state_f:
            state_f.write(json.dumps(saved_state))

        store = state_store.StateStore(self.state_path)

        # When
        store.load_state()

        # Then
        assert store.state == saved_state

    async def test_on_app_startup(self, homehub_app):
        # Given
        fake_threadpool_executor = mock.Mock(
            spec=concurrent.futures.ThreadPoolExecutor,
        )

        with mock.patch.object(concurrent.futures, 'ThreadPoolExecutor') as mock_threadpool_executor:
            mock_threadpool_executor.return_value = fake_threadpool_executor

            store = state_store.StateStore(self.state_path)

            # When
            await store.on_app_startup(homehub_app)

            # Then
            assert store.threadpool == fake_threadpool_executor
            mock_threadpool_executor.assert_called_with(max_workers=1)

    async def test_on_app_cleanup(self, homehub_app):
        # Given
        store = state_store.StateStore(self.state_path)
        store.threadpool = mock.Mock(
            spec=concurrent.futures.ThreadPoolExecutor,
        )

        # When
        await store.on_app_cleanup(homehub_app)

        # Then
        assert store.threadpool.shutdown.called is True

    def test_get(self):
        # Given
        store = state_store.StateStore(self.state_path)

        # When
        result = store.get()

        # Then
        assert result is store.state

    def test_do_save(self):
        # Given
        state_to_save = {
            'backend': {
                'spam': True,
            },
            'frontend': {
                'eggs': True,
            },
        }

        store = state_store.StateStore(self.state_path)

        # When
        store.do_save(state_to_save)

        # Then
        saved_state = None
        with codecs.open(self.state_path, 'r', 'utf-8') as state_f:
            saved_state = json.load(state_f)

        assert saved_state == state_to_save

    def test_save(self):
        # Given
        state_to_save = {
            'backend': {
                'spam': True,
            },
            'frontend': {
                'eggs': True,
            },
        }

        store = state_store.StateStore(self.state_path)
        store.threadpool = mock.Mock(
            spec=concurrent.futures.ThreadPoolExecutor,
        )

        # When
        store.save(state_to_save)

        # Then
        assert store.state == state_to_save
        store.threadpool.submit.assert_called_with(
            store.do_save, state_to_save,
        )
