"""
ARCHES - a program developed to inventory and manage immovable cultural heritage.
Copyright (C) 2013 J. Paul Getty Trust and World Monuments Fund

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.

You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
"""

import os
import json
import csv
from django.test.utils import captured_stdout
from arches.app.utils.data_management.resources.formats.csvfile import (
    CsvWriter,
    MissingConfigException,
)
from operator import itemgetter
from tests.base_test import ArchesTestCase
from arches.app.utils.skos import SKOSReader
from arches.app.utils.betterJSONSerializer import JSONDeserializer
from arches.app.utils.data_management.resources.importer import BusinessDataImporter
from arches.app.utils.data_management.resources.exporter import (
    ResourceExporter as BusinessDataExporter,
)
from arches.app.utils.data_management.resource_graphs.importer import (
    import_graph as ResourceGraphImporter,
)


# these tests can be run from the command line via
# python manage.py test tests.exporter.resource_export_tests --settings="tests.test_settings"


class BusinessDataExportTests(ArchesTestCase):
    @classmethod
    def setUpTestData(cls):
        super().setUpTestData()

        skos = SKOSReader()
        rdf = skos.read_file("tests/fixtures/data/concept_label_test_scheme.xml")
        ret = skos.save_concepts_from_skos(rdf)

        skos = SKOSReader()
        rdf = skos.read_file("tests/fixtures/data/concept_label_test_collection.xml")
        ret = skos.save_concepts_from_skos(rdf)

        with open(
            os.path.join("tests/fixtures/resource_graphs/resource_export_test.json"),
            "r",
        ) as f:
            archesfile = JSONDeserializer().deserialize(f)
        ResourceGraphImporter(archesfile["graph"])

    def test_invalid_writer_config(self):
        with self.assertRaises(MissingConfigException):
            CsvWriter()

    def test_csv_export(self):
        with captured_stdout():
            BusinessDataImporter(
                "tests/fixtures/data/csv/resource_export_test.csv"
            ).import_business_data()

        export = BusinessDataExporter(
            "csv",
            configs="tests/fixtures/data/csv/resource_export_test.mapping",
            single_file=True,
        ).export(languages="en")

        csv_output = list(
            csv.DictReader(export[0]["outputfile"].getvalue().split("\r\n"))
        )[0]
        csvinputfile = "tests/fixtures/data/csv/resource_export_test.csv"
        with open(csvinputfile, "r", encoding="utf-8") as f:
            csv_input = list(
                csv.DictReader(
                    f,
                    restkey="ADDITIONAL",
                    restval="MISSING",
                )
            )[0]

        self.assertDictEqual(dict(csv_input), dict(csv_output))

    def test_json_export(self):
        def deep_sort(obj):
            """
            Recursively sort list or dict nested lists. Taken from
            https://stackoverflow.com/questions/18464095/how-to-achieve-assertdictequal-with-assertsequenceequal-applied-to-values
            """

            if isinstance(obj, dict):
                _sorted = {}
                for key in sorted(obj):
                    _sorted[key] = deep_sort(obj[key])

            elif isinstance(obj, list):
                new_list = []
                for val in obj:
                    new_list.append(deep_sort(val))
                if all(
                    isinstance(item, dict) and "resourceinstance" in item
                    for item in new_list
                ):
                    _sorted = sorted(
                        new_list,
                        key=lambda item: item["resourceinstance"]["resourceinstanceid"],
                    )
                else:
                    try:
                        _sorted = sorted(new_list, key=itemgetter("tileid"))
                    except:
                        _sorted = new_list

            else:
                _sorted = obj

            return _sorted

        with captured_stdout():
            BusinessDataImporter(
                "tests/fixtures/data/json/resource_export_business_data_truth.json"
            ).import_business_data()
        export = BusinessDataExporter("json").export(
            "ab74af76-fa0e-11e6-9e3e-026d961c88e6"
        )

        json_export = deep_sort(json.loads(export[0]["outputfile"].getvalue()))
        with open(
            "tests/fixtures/data/json/resource_export_business_data_truth.json"
        ) as f:
            json_truth = deep_sort(json.load(f))

        # removes generated graph_publication_id
        for resource_data in json_export["business_data"]["resources"]:
            if resource_data["resourceinstance"]["graph_publication_id"]:
                del resource_data["resourceinstance"]["graph_publication_id"]

        self.maxDiff = None
        self.assertDictEqual(json_export, json_truth)
