import os
import json
from unittest import TestCase

from mock import patch, MagicMock, mock_open, call
from elasticsearch import Elasticsearch, NotFoundError as ElasticsearchNotFoundError

from distribution import app
from distribution.managers.backends import ElasticBackend
from elasticsearch.client.indices import IndicesClient
from elasticsearch.client.cluster import ClusterClient


class ElasticBackendTestCase(TestCase):
    def setUp(self):
        self.backend = ElasticBackend()
        self.backend.indices = {
            'distribution_feed': {
                'filename': 'managers/resources/elastic/fake_index_feed.json',
                'real_index_name': None
            },
        }
        self.elasticsearch = MagicMock(Elasticsearch)
        self.elasticsearch.indices = MagicMock(IndicesClient)
        self.elasticsearch.cluster = MagicMock(ClusterClient)

    @patch('distribution.managers.backends.elastic.reindex')
    @patch('distribution.managers.backends.elastic.Elasticsearch')
    def test_init(self, elastic_mock, reindex_mock):
        self.elasticsearch.indices.exists.return_value = False
        self.elasticsearch.indices.exists_alias.return_value = True
        self.elasticsearch.cluster.health.side_effect = [{'status': 'red'}, {'status': 'green'}]
        elastic_mock.return_value = self.elasticsearch
        read_data = {'key': 'value'}
        index_name = 'distribution_feed'
        real_index_name = ('distribution_feed_9724c1e20e6e3e4d7f57ed25f9d4efb006e508590d528c90da59'
                           '7f6a775c13e5')
        self.elasticsearch.indices.get_alias.return_value = {real_index_name: ''}
        exists_calls = [
            call(real_index_name),
            call('distribution_feed')
        ]

        open_mock = mock_open(read_data=json.dumps(read_data))
        with patch('distribution.managers.backends.elastic.open', open_mock, create=True):

            self.backend.client

        open_mock.assert_called_once_with(
            os.path.join(app.config['APP_ROOT'],
                         'managers/resources/elastic/fake_index_feed.json'),
            'r'
        )
        handler = open_mock()
        handler.read.assert_called_once_with()
        self.assertEqual(
            self.backend.indices[index_name]['real_index_name'], real_index_name)
        self.elasticsearch.indices.exists.assert_has_calls(exists_calls)
        self.elasticsearch.indices.create.assert_called_once_with(real_index_name, read_data)
        self.elasticsearch.indices.exists_alias.assert_called_once_with(index_name)
        reindex_mock.assert_called_once_with(self.backend.client, index_name, real_index_name)
        self.elasticsearch.indices.delete.assert_called_once_with(index_name)
        self.elasticsearch.indices.put_alias.assert_called_once_with(index_name, real_index_name)
        self.elasticsearch.indices.get_alias.assert_called_once_with(index_name)

    @patch('distribution.managers.backends.elastic.reindex')
    @patch('distribution.managers.backends.elastic.Elasticsearch')
    def test_init_delete_error_and_alias_exists_and_wrong_index(self, elastic_mock, reindex_mock):
        self.elasticsearch.indices.delete.side_effect = ElasticsearchNotFoundError
        self.elasticsearch.indices.exists.side_effect = [False, True]
        self.elasticsearch.indices.exists_alias.return_value = True
        self.elasticsearch.cluster.health.return_value = {'status': 'green'}
        elastic_mock.return_value = self.elasticsearch
        read_data = {'key': 'value'}
        index_name = 'distribution_feed'
        real_index_name = ('distribution_feed_9724c1e20e6e3e4d7f57ed25f9d4efb006e508590d528c90da59'
                           '7f6a775c13e5')
        self.elasticsearch.indices.get_alias.return_value = {'distribution_feed_old_index': ''}
        exists_calls = [
            call(real_index_name),
            call('distribution_feed')
        ]

        open_mock = mock_open(read_data=json.dumps(read_data))
        with patch('distribution.managers.backends.elastic.open', open_mock, create=True):
            self.backend.client

        open_mock.assert_called_once_with(
            os.path.join(app.config['APP_ROOT'],
                         'managers/resources/elastic/fake_index_feed.json'),
            'r'
        )
        handler = open_mock()
        handler.read.assert_called_once_with()
        self.assertEqual(
            self.backend.indices[index_name]['real_index_name'], real_index_name)
        self.elasticsearch.indices.exists.assert_has_calls(exists_calls)
        self.elasticsearch.indices.create.assert_called_once_with(real_index_name, read_data)
        self.elasticsearch.indices.exists_alias.assert_called_once_with(name=index_name)
        reindex_mock.assert_called_once_with(self.backend.client, index_name, real_index_name)
        self.elasticsearch.indices.delete.assert_called_once_with(index_name)
        self.elasticsearch.indices.put_alias.assert_not_called()
        self.elasticsearch.indices.get_alias.assert_called_once_with(index_name)

    @patch('distribution.managers.backends.elastic.reindex')
    @patch('distribution.managers.backends.elastic.Elasticsearch')
    def test_init_normal(self, elastic_mock, reindex_mock):
        self.elasticsearch.indices.exists.return_value = True
        self.elasticsearch.indices.exists_alias.return_value = True
        self.elasticsearch.cluster.health.return_value = {'status': 'green'}
        elastic_mock.return_value = self.elasticsearch
        read_data = {'key': 'value'}
        index_name = 'distribution_feed'
        real_index_name = ('distribution_feed_9724c1e20e6e3e4d7f57ed25f9d4efb006e508590d528c90da59'
                           '7f6a775c13e5')
        self.elasticsearch.indices.get_alias.return_value = {real_index_name: ''}
        exists_calls = [
            call(real_index_name),
            call('distribution_feed')
        ]

        open_mock = mock_open(read_data=json.dumps(read_data))
        with patch('distribution.managers.backends.elastic.open', open_mock, create=True):

            self.backend.client

        open_mock.assert_called_once_with(
            os.path.join(app.config['APP_ROOT'],
                         'managers/resources/elastic/fake_index_feed.json'),
            'r'
        )
        handler = open_mock()
        handler.read.assert_called_once_with()
        self.assertEqual(
            self.backend.indices[index_name]['real_index_name'], real_index_name)
        self.elasticsearch.indices.exists.assert_has_calls(exists_calls)
        self.elasticsearch.indices.create.assert_not_called()
        self.elasticsearch.indices.exists_alias.assert_not_called()
        reindex_mock.assert_not_called()
        self.elasticsearch.indices.delete.assert_not_called()
        self.elasticsearch.indices.put_alias.assert_not_called()
        self.elasticsearch.indices.get_alias.assert_called_once_with(index_name)

    @patch('distribution.managers.backends.elastic.reindex')
    @patch('distribution.managers.backends.elastic.Elasticsearch')
    def test_init(self, elastic_mock, reindex_mock):
        self.elasticsearch.indices.exists.side_effect = [False, True]
        self.elasticsearch.indices.exists_alias.return_value = False
        self.elasticsearch.cluster.health.return_value = {'status': 'green'}
        elastic_mock.return_value = self.elasticsearch
        read_data = {'key': 'value'}
        index_name = 'distribution_feed'
        real_index_name = ('distribution_feed_9724c1e20e6e3e4d7f57ed25f9d4efb006e508590d528c90da59'
                           '7f6a775c13e5')
        self.elasticsearch.indices.get_alias.return_value = {real_index_name: ''}
        exists_calls = [
            call(real_index_name),
            call('distribution_feed')
        ]

        open_mock = mock_open(read_data=json.dumps(read_data))
        with patch('distribution.managers.backends.elastic.open', open_mock, create=True):
            self.backend.client

        open_mock.assert_called_once_with(
            os.path.join(app.config['APP_ROOT'],
                         'managers/resources/elastic/fake_index_feed.json'),
            'r'
        )
        handler = open_mock()
        handler.read.assert_called_once_with()
        self.assertEqual(
            self.backend.indices[index_name]['real_index_name'], real_index_name)
        self.elasticsearch.indices.exists.assert_has_calls(exists_calls)
        self.elasticsearch.indices.create.assert_called_once_with(real_index_name, read_data)
        self.elasticsearch.indices.exists_alias.assert_called_once_with(name=index_name)
        reindex_mock.assert_not_called()
        self.elasticsearch.indices.delete.assert_called_once_with(index_name)
        self.elasticsearch.indices.put_alias.assert_not_called()
        self.elasticsearch.indices.get_alias.assert_called_once_with(index_name)
