"""Showcase Tests"""
import copy
import json
from os.path import join

import pytest
from hdx.utilities.dictandlist import merge_two_dictionaries
from hdx.utilities.loader import load_yaml

from hdx.api.configuration import Configuration
from hdx.data.hdxobject import HDXError
from hdx.data.showcase import Showcase

from . import MockResponse
from .test_vocabulary import vocabulary_mockshow

showcase_resultdict = {
    "relationships_as_object": [],
    "num_tags": 2,
    "id": "05e392bf-04e0-4ca6-848c-4e87bba10746",
    "metadata_created": "2017-07-03T07:50:49.474517",
    "metadata_modified": "2017-07-03T08:12:43.726624",
    "author": "Dan Mihaila",
    "author_email": "dan@gmail.com",
    "state": "active",
    "creator_user_id": "060468e4-2f33-4488-8504-c4b10cc34821",
    "type": "showcase",
    "resources": [],
    "tags": [
        {
            "vocabulary_id": "4381925f-0ae9-44a3-b30d-cae35598757b",
            "state": "active",
            "display_name": "economics",
            "id": "e1ff4b78-efcf-40e7-888b-7342169068e1",
            "name": "economics",
        },
        {
            "vocabulary_id": "4381925f-0ae9-44a3-b30d-cae35598757b",
            "state": "active",
            "display_name": "health",
            "id": "8a43b269-78cb-4a85-a4ce-7e3e928bc487",
            "name": "health",
        },
    ],
    "groups": [],
    "relationships_as_subject": [],
    "total_res_downloads": 0,
    "num_datasets": 2,
    "showcase_notes_formatted": "<p>lalala</p>",
    "image_url": "visual.png",
    "revision_id": "cc64364b-ede2-400a-bb9f-8e585a4f6399",
    "notes": "My Showcase",
    "url": "http://visualisation/url/",
    "title": "MyShowcase1",
    "image_display_url": "http://myvisual/visual.png",
    "name": "showcase-1",
}

datasetsdict = load_yaml(
    join("tests", "fixtures", "dataset_search_results.yml")
)
allsearchdict = load_yaml(
    join("tests", "fixtures", "showcase_all_search_results.yml")
)


def mockshow(url, datadict):
    if "package_list" in url:
        result = json.dumps(datasetsdict["results"])
        return MockResponse(
            200,
            '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_package_list"}'
            % result,
        )
    if "_show" not in url:
        return MockResponse(
            404,
            '{"success": false, "error": {"message": "TEST ERROR: Not show", "__type": "TEST ERROR: Not Show Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_show"}',
        )
    result = json.dumps(showcase_resultdict)
    if (
        datadict["id"] == "05e392bf-04e0-4ca6-848c-4e87bba10746"
        or datadict["id"] == "TEST1"
    ):
        return MockResponse(
            200,
            '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_show"}'
            % result,
        )
    if datadict["id"] == "TEST2":
        return MockResponse(
            404,
            '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_show"}',
        )
    if datadict["id"] == "TEST3":
        return MockResponse(
            200,
            '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_show"}',
        )
    return MockResponse(
        404,
        '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_show"}',
    )


def mockallsearch(url, datadict):
    if "search" not in url:
        return MockResponse(
            404,
            '{"success": false, "error": {"message": "TEST ERROR: Not search", "__type": "TEST ERROR: Not Search Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=package_search"}',
        )
    if "metadata_modified" in datadict["fq"]:
        newsearchdict = copy.deepcopy(allsearchdict)
        newsearchdict["results"] = [newsearchdict["results"][0]]
        return MockResponse(
            200,
            '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=package_search"}'
            % json.dumps(newsearchdict),
        )
    if datadict["q"] == "ACLED":
        newsearchdict = copy.deepcopy(allsearchdict)
        if datadict["rows"] == 11:
            newsearchdict["results"].append(newsearchdict["results"][0])
        elif datadict["rows"] == 6:
            if datadict["start"] == 2:
                newsearchdict["results"] = newsearchdict["results"][2:8]
        elif datadict["rows"] == 5:
            if datadict["start"] == 0:
                newsearchdict["count"] = 5
                newsearchdict["results"] = newsearchdict["results"][:5]
            elif datadict["start"] == 5:
                newsearchdict["count"] = 6  # return wrong count
                newsearchdict["results"] = newsearchdict["results"][:5]
            else:
                newsearchdict["count"] = 0
                newsearchdict["results"] = list()
        result = json.dumps(newsearchdict)
        return MockResponse(
            200,
            '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=package_search"}'
            % result,
        )
    if datadict["q"] == "*:*":
        newsearchdict = copy.deepcopy(allsearchdict)
        newsearchdict["results"].extend(
            copy.deepcopy(newsearchdict["results"])
        )
        for i, x in enumerate(newsearchdict["results"]):
            x["id"] = f'{x["id"]}{i}'
        return MockResponse(
            200,
            '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=package_search"}'
            % json.dumps(newsearchdict),
        )
    if datadict["q"] == '"':
        return MockResponse(
            404,
            '{"success": false, "error": {"message": "Validation Error", "__type": "Validation Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=package_search"}',
        )
    if datadict["q"] == "ajyhgr":
        return MockResponse(
            200,
            '{"success": true, "result": {"count": 0, "results": []}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=package_search"}',
        )
    return MockResponse(
        404,
        '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=package_search"}',
    )


class TestShowcase:
    showcase_data = {
        "title": "MyShowcase1",
        "notes": "My Showcase",
        "package_id": "6f36a41c-f126-4b18-aaaf-6c2ddfbc5d4d",
        "image_url": "http://myvisual/visual.png",
        "name": "showcase-1",
        "url": "http://visualisation/url/",
        "tags": [{"name": "economy"}, {"name": "health"}],
        "dataset_ids": ["a89c6260-6392-416e-bcbc-eb2c5f1d7add"],
    }
    association = None

    @pytest.fixture(scope="class")
    def static_yaml(self):
        return join("tests", "fixtures", "config", "hdx_showcase_static.yml")

    @pytest.fixture(scope="class")
    def static_json(self):
        return join("tests", "fixtures", "config", "hdx_showcase_static.json")

    @pytest.fixture(scope="function")
    def read(self):
        class MockSession:
            @staticmethod
            def post(url, data, headers, files, allow_redirects, auth=None):
                datadict = json.loads(data.decode("utf-8"))
                if "association_delete" in url:
                    TestShowcase.association = "delete"
                    return MockResponse(
                        200,
                        '{"success": true, "result": null, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_package_association_delete"}',
                    )
                elif "association_create" in url:
                    TestShowcase.association = "create"
                    result = json.dumps(datadict)
                    return MockResponse(
                        200,
                        '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_package_association_create"}'
                        % result,
                    )
                return mockshow(url, datadict)

        Configuration.read().remoteckan().session = MockSession()

    @pytest.fixture(scope="function")
    def post_create(self):
        class MockSession:
            @staticmethod
            def post(url, data, headers, files, allow_redirects, auth=None):
                datadict = json.loads(data.decode("utf-8"))
                if "vocabulary" in url:
                    return vocabulary_mockshow(url, datadict)
                if url.endswith("show") or "list" in url:
                    return mockshow(url, datadict)
                if "create" not in url:
                    return MockResponse(
                        404,
                        '{"success": false, "error": {"message": "TEST ERROR: Not create", "__type": "TEST ERROR: Not Create Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_create"}',
                    )

                resultdictcopy = copy.deepcopy(showcase_resultdict)
                resultdictcopy["state"] = datadict["state"]
                result = json.dumps(resultdictcopy)
                if datadict["title"] == "MyShowcase1":
                    return MockResponse(
                        200,
                        '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_create"}'
                        % result,
                    )
                if datadict["title"] == "MyShowcase2":
                    return MockResponse(
                        404,
                        '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_create"}',
                    )
                if datadict["title"] == "MyShowcase3":
                    return MockResponse(
                        200,
                        '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_create"}',
                    )

                return MockResponse(
                    404,
                    '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_create"}',
                )

        Configuration.read().remoteckan().session = MockSession()

    @pytest.fixture(scope="function")
    def post_update(self):
        class MockSession:
            @staticmethod
            def post(url, data, headers, files, allow_redirects, auth=None):
                datadict = json.loads(data.decode("utf-8"))
                if "vocabulary" in url:
                    return vocabulary_mockshow(url, datadict)
                if url.endswith("show") or "list" in url:
                    return mockshow(url, datadict)
                if "update" not in url:
                    return MockResponse(
                        404,
                        '{"success": false, "error": {"message": "TEST ERROR: Not update", "__type": "TEST ERROR: Not Update Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_update"}',
                    )
                resultdictcopy = copy.deepcopy(showcase_resultdict)
                merge_two_dictionaries(resultdictcopy, datadict)

                result = json.dumps(resultdictcopy)
                if datadict["title"] == "MyShowcase1":
                    return MockResponse(
                        200,
                        '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_update"}'
                        % result,
                    )
                if datadict["title"] == "MyShowcase2":
                    return MockResponse(
                        404,
                        '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_update"}',
                    )
                if datadict["title"] == "MyShowcase3":
                    return MockResponse(
                        200,
                        '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_update"}',
                    )

                return MockResponse(
                    404,
                    '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_update"}',
                )

        Configuration.read().remoteckan().session = MockSession()

    @pytest.fixture(scope="function")
    def post_delete(self):
        class MockSession:
            @staticmethod
            def post(url, data, headers, files, allow_redirects, auth=None):
                decodedata = data.decode("utf-8")
                datadict = json.loads(decodedata)
                if url.endswith("show") or "list" in url:
                    return mockshow(url, datadict)
                if "delete" not in url:
                    return MockResponse(
                        404,
                        '{"success": false, "error": {"message": "TEST ERROR: Not delete", "__type": "TEST ERROR: Not Delete Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_delete"}',
                    )
                if datadict["id"] == "05e392bf-04e0-4ca6-848c-4e87bba10746":
                    return MockResponse(
                        200,
                        '{"success": true, "result": %s, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_delete"}'
                        % decodedata,
                    )

                return MockResponse(
                    404,
                    '{"success": false, "error": {"message": "Not found", "__type": "Not Found Error"}, "help": "http://test-data.humdata.org/api/3/action/help_show?name=ckanext_showcase_delete"}',
                )

        Configuration.read().remoteckan().session = MockSession()

    @pytest.fixture(scope="function")
    def allsearch(self):
        class MockSession:
            @staticmethod
            def post(url, data, headers, files, allow_redirects, auth=None):
                datadict = json.loads(data.decode("utf-8"))
                return mockallsearch(url, datadict)

        Configuration.read().remoteckan().session = MockSession()

    def test_read_from_hdx(self, configuration, read):
        showcase = Showcase.read_from_hdx(
            "05e392bf-04e0-4ca6-848c-4e87bba10746"
        )
        assert showcase["id"] == "05e392bf-04e0-4ca6-848c-4e87bba10746"
        assert showcase["title"] == "MyShowcase1"
        showcase = Showcase.read_from_hdx("TEST2")
        assert showcase is None
        showcase = Showcase.read_from_hdx("TEST3")
        assert showcase is None

    def test_create_in_hdx(self, configuration, post_create):
        showcase = Showcase()
        with pytest.raises(HDXError):
            showcase.create_in_hdx()
        showcase["id"] = "05e392bf-04e0-4ca6-848c-4e87bba10746"
        showcase["title"] = "LALA"
        with pytest.raises(HDXError):
            showcase.create_in_hdx()

        showcase_data = copy.deepcopy(TestShowcase.showcase_data)
        showcase = Showcase(showcase_data)
        showcase.create_in_hdx()
        assert showcase["id"] == "05e392bf-04e0-4ca6-848c-4e87bba10746"
        assert showcase["state"] == "active"

        showcase_data["title"] = "MyShowcase2"
        showcase = Showcase(showcase_data)
        with pytest.raises(HDXError):
            showcase.create_in_hdx()

        showcase_data["title"] = "MyShowcase3"
        showcase = Showcase(showcase_data)
        with pytest.raises(HDXError):
            showcase.create_in_hdx()

    def test_update_in_hdx(self, configuration, post_update):
        showcase = Showcase()
        showcase["id"] = "NOTEXIST"
        with pytest.raises(HDXError):
            showcase.update_in_hdx()
        showcase["title"] = "LALA"
        with pytest.raises(HDXError):
            showcase.update_in_hdx()

        showcase = Showcase.read_from_hdx(
            "05e392bf-04e0-4ca6-848c-4e87bba10746"
        )
        assert showcase["id"] == "05e392bf-04e0-4ca6-848c-4e87bba10746"
        assert showcase["title"] == "MyShowcase1"

        showcase["name"] = "TEST1"
        showcase["notes"] = "lalalala"
        showcase.update_in_hdx()
        assert showcase["name"] == "TEST1"
        assert showcase["notes"] == "lalalala"
        assert showcase["state"] == "active"
        expected = copy.deepcopy(showcase_resultdict)
        expected["notes"] = "lalalala"
        expected["name"] = "TEST1"
        expected["tags"] = [
            {
                "name": "economics",
                "vocabulary_id": "4381925f-0ae9-44a3-b30d-cae35598757b",
            },
            {
                "name": "health",
                "vocabulary_id": "4381925f-0ae9-44a3-b30d-cae35598757b",
            },
        ]
        assert showcase.get_old_data_dict() == expected

        showcase["name"] = "NOTEXIST"
        with pytest.raises(HDXError):
            showcase.update_in_hdx()

        del showcase["name"]
        with pytest.raises(HDXError):
            showcase.update_in_hdx()

        showcase_data = copy.deepcopy(TestShowcase.showcase_data)
        showcase_data["title"] = "MyShowcase1"
        showcase_data["name"] = "TEST1"
        showcase = Showcase(showcase_data)
        showcase.create_in_hdx()
        assert showcase["name"] == "TEST1"
        assert showcase["notes"] == "My Showcase"
        assert showcase["state"] == "active"

    def test_delete_from_hdx(self, configuration, post_delete):
        showcase = Showcase.read_from_hdx(
            "05e392bf-04e0-4ca6-848c-4e87bba10746"
        )
        showcase.delete_from_hdx()
        del showcase["id"]
        with pytest.raises(HDXError):
            showcase.delete_from_hdx()

    def test_update_yaml(self, configuration, static_yaml):
        showcase_data = copy.deepcopy(TestShowcase.showcase_data)
        showcase = Showcase(showcase_data)
        assert showcase["title"] == "MyShowcase1"
        assert showcase["name"] == "showcase-1"
        showcase.update_from_yaml(static_yaml)
        assert showcase["title"] == "MyShowcase1"
        assert showcase["name"] == "my-showcase-1"

    def test_update_json(self, configuration, static_json):
        showcase_data = copy.deepcopy(TestShowcase.showcase_data)
        showcase = Showcase(showcase_data)
        assert showcase["title"] == "MyShowcase1"
        assert showcase["name"] == "showcase-1"
        showcase.update_from_json(static_json)
        assert showcase["title"] == "MyShowcase1"
        assert showcase["name"] == "new-showcase-1"

    def test_tags(self, configuration):
        showcase_data = copy.deepcopy(TestShowcase.showcase_data)
        showcase = Showcase(showcase_data)
        assert showcase.get_tags() == ["economy", "health"]
        showcase.add_tag("wash")
        assert showcase.get_tags() == [
            "economy",
            "health",
            "water sanitation and hygiene - wash",
        ]
        showcase.add_tags(["sanitation"])
        assert showcase.get_tags() == [
            "economy",
            "health",
            "water sanitation and hygiene - wash",
        ]
        result = showcase.remove_tag("water sanitation and hygiene - wash")
        assert result is True
        assert showcase.get_tags() == ["economy", "health"]
        showcase["tags"] = None
        result = showcase.remove_tag("wash")
        assert result is False

    def test_datasets(self, configuration, read):
        showcase = Showcase.read_from_hdx(
            "05e392bf-04e0-4ca6-848c-4e87bba10746"
        )
        datasets = showcase.get_datasets()
        assert len(datasets) == 10
        assert datasets[0].data == datasetsdict["results"][0]
        dict4 = copy.deepcopy(datasetsdict["results"][4])
        del dict4["resources"]
        assert datasets[4].data == dict4
        TestShowcase.association = None
        showcase.remove_dataset(datasets[0])
        assert TestShowcase.association == "delete"
        TestShowcase.association = None
        assert (
            showcase.add_dataset("a2f32edd-bac2-4940-aa58-49e565041055")
            is True
        )
        assert TestShowcase.association == "create"
        TestShowcase.association = None
        assert (
            showcase.add_datasets(
                [
                    {"id": "a2f32edd-bac2-4940-aa58-49e565041055"},
                    {"id": "6a5aebc1-f5a9-4842-8183-b8118228e71e"},
                ]
            )
            is False
        )
        assert TestShowcase.association == "create"
        TestShowcase.association = None
        assert showcase.add_dataset({"name": "TEST1"}) is True
        assert TestShowcase.association == "create"
        TestShowcase.association = None
        with pytest.raises(HDXError):
            showcase.add_dataset("123")
        with pytest.raises(HDXError):
            showcase.add_dataset(123)

    def test_search_in_hdx(self, configuration, allsearch):
        showcases = Showcase.search_in_hdx("ACLED")
        assert len(showcases) == 10
        showcases = Showcase.search_in_hdx("ACLED", offset=2, limit=6)
        assert len(showcases) == 6
        showcases = Showcase.search_in_hdx(
            fq="metadata_modified:[2018-01-01T00:00:00.000Z TO NOW]"
        )
        assert len(showcases) == 1
        showcases = Showcase.search_in_hdx("ajyhgr")
        assert len(showcases) == 0
        with pytest.raises(HDXError):
            Showcase.search_in_hdx('"')
        with pytest.raises(HDXError):
            Showcase.search_in_hdx("ACLED", rows=11)
        with pytest.raises(HDXError):
            # Test returned row counts per page mismatch (wrong count of 6 purposely in mocksearch)
            Showcase.search_in_hdx("ACLED", page_size=5)

    def test_get_all_showcases(self, configuration, allsearch):
        showcases = Showcase.get_all_showcases()
        assert len(showcases) == 20
