# Copyright 2019 Huawei Technologies Co., Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# ============================================================================
"""
Function:
    Test train visual api.
Usage:
    pytest tests/ut/datavisual
"""
from unittest.mock import Mock, patch

import pytest

from mindinsight.datavisual.processors.graph_processor import GraphProcessor
from mindinsight.datavisual.processors.images_processor import ImageProcessor
from mindinsight.datavisual.processors.scalars_processor import ScalarsProcessor
from mindinsight.datavisual.processors.histogram_processor import HistogramProcessor

from ....utils.tools import get_url
from .conftest import TRAIN_ROUTES


class TestTrainVisual:
    """Test Train Visual APIs."""

    def test_image_metadata_with_params_miss(self, client):
        """Test missing params when getting image metadata."""

        params = dict()
        url = get_url(TRAIN_ROUTES['image_metadata'], params)

        response = client.get(url)

        assert response.status_code == 400

        response = response.get_json()
        assert response['error_code'] == '50540003'
        assert response['error_msg'] == "Param missing. 'train_id' is required."

        train_id = "aa"

        params = dict(train_id=train_id)
        url = get_url(TRAIN_ROUTES['image_metadata'], params)

        response = client.get(url)

        assert response.status_code == 400

        response = response.get_json()
        assert response['error_code'] == '50540003'
        assert response['error_msg'] == "Param missing. 'tag' is required."

    @patch.object(ImageProcessor, 'get_metadata_list')
    def test_image_metadata_with_params_success(self, mock_processor, client):
        """
        Parsing available params to get image metadata.

        Test Params:
        request route: GET('/v1/mindinsight/datavisual/image/metadata').
        request params: train_id.
        request params: tag.

        Expect:
        response status code: 400.
        response json: {
            'error_code': '50545006',
            'error_message': "Invalid parameter value. 'plugin_name' only can
                             be one of ['graph', 'image', 'scalar']"
        }.
        """
        mock_get_metadata_list = Mock()
        mock_get_metadata_list.return_value = {
            "metadatas": [{
                "height": 224,
                "step": 1,
                "wall_time": 1572058058.1175,
                "width": 448
            }]
        }
        params = dict(train_id='123', tag='123')
        url = get_url(TRAIN_ROUTES['image_metadata'], params)

        mock_processor.side_effect = mock_get_metadata_list
        response = client.get(url)

        assert response.status_code == 200
        response = response.get_json()
        expected_response = {"metadatas": [{"height": 224, "step": 1, "wall_time": 1572058058.1175, "width": 448}]}
        assert expected_response == response

    def test_single_image_with_params_miss(self, client):
        """
        Test missing params when getting single image.

        Test Params:
        request route: GET('/v1/mindinsight/datavisual/image/single-image').
        request params: train_id.
        request params: tag.
        request params: step.

        Expect:
        response status code: 400.
        response json: {
            'error_code': '50540003',
            'error_message': "Param missing. 'train_id' is required."
        }.
        """
        params = dict()
        url = get_url(TRAIN_ROUTES['image_single_image'], params)
        response = client.get(url)
        assert response.status_code == 400
        response = response.get_json()
        assert response['error_code'] == '50540003'
        assert response['error_msg'] == "Param missing. 'train_id' is required."

        params = dict(train_id='123')
        url = get_url(TRAIN_ROUTES['image_single_image'], params)
        response = client.get(url)
        assert response.status_code == 400
        response = response.get_json()
        assert response['error_code'] == '50540003'
        assert response['error_msg'] == "Param missing. 'tag' is required."

        params = dict(train_id='123', tag='456')
        url = get_url(TRAIN_ROUTES['image_single_image'], params)
        response = client.get(url)
        assert response.status_code == 400
        response = response.get_json()
        assert response['error_code'] == '50540003'
        assert response['error_msg'] == "Param missing. 'step' is required."

    def test_single_image_with_step_not_int(self, client):
        """
        Test the `tag` param is not int when getting single image.

        Test Params:
        request route: GET('/v1/mindinsight/datavisual/image/single-image').
        request params: train_id.
        request params: tag.
        request params: step.

        Expect:
        response status code: 400.
        response json: {
            'error_code': '50540001',
            'error_message': "Invalid parameter type. 'step' expect Integer type."
        }.
        """
        params = dict(train_id='123', tag='456', step='abc')
        url = get_url(TRAIN_ROUTES['image_single_image'], params)
        response = client.get(url)
        assert response.status_code == 400
        response = response.get_json()
        assert response['error_code'] == '50540001'
        assert response['error_msg'] == "Invalid parameter type. 'step' expect Integer type."

    @patch.object(ImageProcessor, 'get_single_image')
    def test_single_image_with_params_success(self, mock_processor, client):
        """Test getting single image with params successfully."""
        mock_get_single_image = Mock(return_value=b'123')
        params = dict(train_id='123', tag='123', step=1)
        url = get_url(TRAIN_ROUTES['image_single_image'], params)

        mock_processor.side_effect = mock_get_single_image
        response = client.get(url)

        assert response.status_code == 200
        assert response.data == b'123'

    def test_scalar_metadata_with_params_is_none(self, client):
        """Parsing unavailable params to get scalar metadata."""
        params = dict()
        url = get_url(TRAIN_ROUTES['scalar_metadata'], params)

        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540003'
        assert results['error_msg'] == "Param missing. 'train_id' is required."

        train_id = "aa"
        params = dict(train_id=train_id)
        url = get_url(TRAIN_ROUTES['scalar_metadata'], params)
        response = client.get(url)
        results = response.get_json()

        assert response.status_code == 400
        assert results['error_code'] == '50540003'
        assert results['error_msg'] == "Param missing. 'tag' is required."

    @patch.object(ScalarsProcessor, 'get_metadata_list')
    def test_scalar_metadata_success(self, mock_scalar_processor, client):
        """Parsing available params to get scalar metadata."""
        get_metadata_list = Mock(return_value={'metadatas': [{'value': 1}]})
        mock_scalar_processor.side_effect = get_metadata_list

        test_train_id = "aa"
        test_tag = "bb"
        params = dict(train_id=test_train_id, tag=test_tag)
        url = get_url(TRAIN_ROUTES['scalar_metadata'], params)
        response = client.get(url)
        assert response.status_code == 200
        results = response.get_json()
        assert results == dict(metadatas=[dict(value=1)])

    def test_graph_nodes_with_train_id_is_none(self, client):
        """Test getting graph nodes when train_id is none."""
        params = dict()
        url = get_url(TRAIN_ROUTES['graph_nodes'], params)

        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540003'
        assert results['error_msg'] == "Param missing. 'train_id' is required."

    @patch.object(GraphProcessor, '__init__')
    @patch.object(GraphProcessor, 'list_nodes')
    def test_graph_nodes_success(self, mock_list_nodes_func, mock_init_func, client):
        """Test getting graph nodes successfully."""

        def mock_list_nodes(scope):
            return dict(scope=scope)

        mock_list_nodes_func.side_effect = mock_list_nodes

        mock_init = Mock(return_value=None)
        mock_init_func.side_effect = mock_init

        test_train_id = 'aaa'
        test_node_name = 'bbb'
        params = dict(train_id=test_train_id, name=test_node_name)
        url = get_url(TRAIN_ROUTES['graph_nodes'], params)

        response = client.get(url)
        assert response.status_code == 200
        results = response.get_json()
        assert results == dict(scope=test_node_name)

    def test_graph_node_names_with_train_id_is_none(self, client):
        """Test getting graph node names with train id is none."""
        params = dict()
        url = get_url(TRAIN_ROUTES['graph_nodes_names'], params)

        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540003'
        assert results['error_msg'] == "Param missing. 'train_id' is required."

    @patch.object(GraphProcessor, '__init__')
    def test_graph_node_names_with_param_type(self, mock_graph_processor, client):
        """Test getting graph node names with param type."""
        mock_init = Mock(return_value=None)
        mock_graph_processor.side_effect = mock_init

        test_train_id = 'aaa'
        params = dict(train_id=test_train_id, offset='a', limit='b')
        url = get_url(TRAIN_ROUTES['graph_nodes_names'], params)

        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540001'
        assert results['error_msg'] == "Invalid parameter type. 'offset' expect Integer type."

        params = dict(train_id=test_train_id, offset=1, limit='c')
        url = get_url(TRAIN_ROUTES['graph_nodes_names'], params)

        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540001'
        assert results['error_msg'] == "Invalid parameter type. 'limit' expect Integer type."

    @patch.object(GraphProcessor, '__init__')
    def test_graph_node_names_with_invalid_offset(self, mock_graph_processor, client):
        """Test getting graph node names with invalid offset."""
        mock_init = Mock(return_value=None)
        mock_graph_processor.side_effect = mock_init

        test_train_id = 'aaa'
        test_offset = -1
        test_limit = 100
        params = dict(train_id=test_train_id, offset=test_offset, limit=test_limit)
        url = get_url(TRAIN_ROUTES['graph_nodes_names'], params)

        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540002'
        assert results['error_msg'] == "Invalid parameter value. 'offset' should " \
                                       "be greater than or equal to 0."

    @pytest.mark.parametrize("limit", [-1, 0, 1001])
    @patch.object(GraphProcessor, '__init__')
    def test_graph_node_names_with_invalid_limit(self, mock_graph_processor, client, limit):
        """Test getting graph node names with invalid limit."""
        mock_init = Mock(return_value=None)
        mock_graph_processor.side_effect = mock_init

        test_train_id = 'aaa'
        params = dict(train_id=test_train_id, limit=limit)
        url = get_url(TRAIN_ROUTES['graph_nodes_names'], params)

        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540002'
        assert results['error_msg'] == "Invalid parameter value. " \
                                       "'limit' should in [1, 1000]."

    @pytest.mark.parametrize(" offset, limit", [(0, 100), (1, 1), (0, 1000)])
    @patch.object(GraphProcessor, '__init__')
    @patch.object(GraphProcessor, 'search_node_names')
    def test_graph_node_names_success(self, mock_graph_processor, mock_graph_processor_1, client, offset, limit):
        """
        Parsing unavailable params to get image metadata.

        Test Params:
        request route: GET('/v1/mindinsight/datavisual/graph/nodes/names').
        request params: plugin_name.

        Expect:
        response status code: 200.
        response json: dict, contains search_content, offset, and limit.
        """

        def mock_search_node_names(search_content, offset, limit):
            return dict(search_content=search_content, offset=int(offset), limit=int(limit))

        mock_graph_processor.side_effect = mock_search_node_names

        mock_init = Mock(return_value=None)
        mock_graph_processor_1.side_effect = mock_init

        test_train_id = "aaa"
        test_search_content = "bbb"
        params = dict(train_id=test_train_id, search=test_search_content, offset=offset, limit=limit)
        url = get_url(TRAIN_ROUTES['graph_nodes_names'], params)
        response = client.get(url)
        assert response.status_code == 200
        results = response.get_json()
        assert results == dict(search_content=test_search_content, offset=int(offset), limit=int(limit))

    def test_graph_search_single_node_with_params_is_wrong(self, client):
        """Test searching graph single node with params is wrong."""
        test_name = 'aaa'
        params = dict(name=test_name)
        url = get_url(TRAIN_ROUTES['graph_single_node'], params)

        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540003'
        assert results['error_msg'] == "Param missing. 'train_id' is required."

    @patch.object(GraphProcessor, '__init__')
    def test_graph_search_single_node_with_params_is_none(self, mock_graph_processor, client):
        """Test searching graph single node with node_name is none."""
        mock_init = Mock(return_value=None)

        mock_graph_processor.side_effect = mock_init
        params = dict()
        url = get_url(TRAIN_ROUTES['graph_single_node'], params)
        response = client.get(url)
        results = response.get_json()

        assert response.status_code == 400
        assert results['error_code'] == '50540003'
        assert results['error_msg'] == "Param missing. 'name' is required."

    @patch.object(GraphProcessor, '__init__')
    @patch.object(GraphProcessor, 'search_single_node')
    def test_graph_search_single_node_success(self, mock_graph_processor, mock_graph_processor_1, client):
        """
        Test searching graph single node successfully.

        Test Params:
        request route: GET('/v1/mindinsight/datavisual//').
        request params: plugin_name.

        Expect:
        response status code: 200.
        response json: name.
        """

        def mock_search_single_node(name):
            return name

        mock_graph_processor.side_effect = mock_search_single_node

        mock_init = Mock(return_value=None)
        mock_graph_processor_1.side_effect = mock_init

        test_train_id = "aaa"
        test_name = 'bbb'
        params = dict(train_id=test_train_id, name=test_name)
        url = get_url(TRAIN_ROUTES['graph_single_node'], params)
        response = client.get(url)
        assert response.status_code == 200
        results = response.get_json()
        assert results == test_name

    def test_histograms_with_params_miss(self, client):
        """Parsing missing params to get histogram data."""
        params = dict()
        url = get_url(TRAIN_ROUTES['histograms'], params)
        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540003'
        assert results['error_msg'] == "Param missing. 'train_id' is required."

        train_id = "aa"
        params = dict(train_id=train_id)
        url = get_url(TRAIN_ROUTES['histograms'], params)
        response = client.get(url)
        results = response.get_json()
        assert response.status_code == 400
        assert results['error_code'] == '50540003'
        assert results['error_msg'] == "Param missing. 'tag' is required."

    @patch.object(HistogramProcessor, 'get_histograms')
    def test_histograms_success(self, mock_histogram_processor, client):
        """Parsing available params to get histogram data."""
        test_train_id = "aa"
        test_tag = "bb"
        expect_resp = {
            'histograms': [{'buckets': [[1, 2, 3]]}],
            'train_id': test_train_id,
            'tag': test_tag
        }
        get_histograms = Mock(return_value=expect_resp)
        mock_histogram_processor.side_effect = get_histograms

        params = dict(train_id=test_train_id, tag=test_tag)
        url = get_url(TRAIN_ROUTES['histograms'], params)
        response = client.get(url)
        assert response.status_code == 200
        results = response.get_json()
        assert results == expect_resp
