#!/usr/bin/python

# SPDX-FileCopyrightText: 2025 Arcangelo Massari <arcangelo.massari@unibo.it>
#
# SPDX-License-Identifier: ISC

# -*- coding: utf-8 -*-
import pickle
import unittest

from rdflib import Graph

from oc_ocdm.graph.graph_set import GraphSet
from oc_ocdm.graph.entities.identifier import Identifier
from oc_ocdm.graph.entities.bibliographic.agent_role import AgentRole
from oc_ocdm.graph.entities.bibliographic.bibliographic_reference import BibliographicReference
from oc_ocdm.graph.entities.bibliographic.bibliographic_resource import BibliographicResource
from oc_ocdm.graph.entities.bibliographic.citation import Citation
from oc_ocdm.graph.entities.bibliographic.discourse_element import DiscourseElement
from oc_ocdm.graph.entities.bibliographic.pointer_list import PointerList
from oc_ocdm.graph.entities.bibliographic.reference_annotation import ReferenceAnnotation
from oc_ocdm.graph.entities.bibliographic.reference_pointer import ReferencePointer
from oc_ocdm.graph.entities.bibliographic.resource_embodiment import ResourceEmbodiment
from oc_ocdm.graph.entities.bibliographic.responsible_agent import ResponsibleAgent


class TestGraphSet(unittest.TestCase):
    resp_agent = 'http://resp_agent.test/'

    def setUp(self):
        self.graph_set = GraphSet("http://test/", "./info_dir/", "", False)

    def test_get_entity(self):
        ar = self.graph_set.add_ar(self.resp_agent)
        ref = ar.res
        result = self.graph_set.get_entity(ref)
        self.assertIsNotNone(result)
        self.assertIs(result, ar)

    def test_add_an(self):
        an = self.graph_set.add_an(self.resp_agent)

        self.assertIsNotNone(an)
        self.assertIsInstance(an, ReferenceAnnotation)
        self.assertEqual(str(an.g.identifier), self.graph_set.g_an)

    def test_add_ar(self):
        ar = self.graph_set.add_ar(self.resp_agent)

        self.assertIsNotNone(ar)
        self.assertIsInstance(ar, AgentRole)
        self.assertEqual(str(ar.g.identifier), self.graph_set.g_ar)

    def test_add_be(self):
        be = self.graph_set.add_be(self.resp_agent)

        self.assertIsNotNone(be)
        self.assertIsInstance(be, BibliographicReference)
        self.assertEqual(str(be.g.identifier), self.graph_set.g_be)

    def test_add_br(self):
        br = self.graph_set.add_br(self.resp_agent)

        self.assertIsNotNone(br)
        self.assertIsInstance(br, BibliographicResource)
        self.assertEqual(str(br.g.identifier), self.graph_set.g_br)

    def test_add_ci(self):
        ci = self.graph_set.add_ci(self.resp_agent)

        self.assertIsNotNone(ci)
        self.assertIsInstance(ci, Citation)
        self.assertEqual(str(ci.g.identifier), self.graph_set.g_ci)

    def test_add_de(self):
        de = self.graph_set.add_de(self.resp_agent)

        self.assertIsNotNone(de)
        self.assertIsInstance(de, DiscourseElement)
        self.assertEqual(str(de.g.identifier), self.graph_set.g_de)

    def test_add_id(self):
        identifier = self.graph_set.add_id(self.resp_agent)

        self.assertIsNotNone(identifier)
        self.assertIsInstance(identifier, Identifier)
        self.assertEqual(str(identifier.g.identifier), self.graph_set.g_id)

    def test_add_pl(self):
        pl = self.graph_set.add_pl(self.resp_agent)

        self.assertIsNotNone(pl)
        self.assertIsInstance(pl, PointerList)
        self.assertEqual(str(pl.g.identifier), self.graph_set.g_pl)

    def test_add_rp(self):
        rp = self.graph_set.add_rp(self.resp_agent)

        self.assertIsNotNone(rp)
        self.assertIsInstance(rp, ReferencePointer)
        self.assertEqual(str(rp.g.identifier), self.graph_set.g_rp)

    def test_add_ra(self):
        ra = self.graph_set.add_ra(self.resp_agent)

        self.assertIsNotNone(ra)
        self.assertIsInstance(ra, ResponsibleAgent)
        self.assertEqual(str(ra.g.identifier), self.graph_set.g_ra)

    def test_add_re(self):
        re = self.graph_set.add_re(self.resp_agent)

        self.assertIsNotNone(re)
        self.assertIsInstance(re, ResourceEmbodiment)
        self.assertEqual(str(re.g.identifier), self.graph_set.g_re)

    def test_graphs(self):
        count = 10
        for i in range(count):
            self.graph_set.add_ar(self.resp_agent)
        result = self.graph_set.graphs()
        self.assertIsNotNone(result)
        self.assertEqual(len(result), count)
        for graph in result:
            self.assertIsInstance(graph, Graph)

    def test_get_graph_iri(self):
        ar = self.graph_set.add_ar(self.resp_agent)
        iri = str(ar.g.identifier)
        result = GraphSet.get_graph_iri(ar.g)
        self.assertIsNotNone(result)
        self.assertEqual(iri, result)

    def test_get_orphans(self):
        br = self.graph_set.add_br(self.resp_agent)
        ar = self.graph_set.add_ar(self.resp_agent)
        ra = self.graph_set.add_ra(self.resp_agent)

        with self.subTest("subtest 1"):
            orphans = self.graph_set.get_orphans()
            self.assertIsNotNone(orphans)

            orphans_set = {o.res for o in orphans}
            self.assertSetEqual({br.res, ar.res, ra.res}, orphans_set)

        with self.subTest("subtest 2"):
            # Here we link br and ar:
            br.has_contributor(ar)

            orphans = self.graph_set.get_orphans()
            self.assertIsNotNone(orphans)

            orphans_set = {o.res for o in orphans}
            self.assertSetEqual({br.res, ra.res}, orphans_set)

        with self.subTest("subtest 3"):
            # Here we link ar and ra:
            ar.is_held_by(ra)

            orphans = self.graph_set.get_orphans()
            self.assertIsNotNone(orphans)

            orphans_set = {o.res for o in orphans}
            self.assertSetEqual({br.res}, orphans_set)

    def test_get_an(self):
        an1 = self.graph_set.add_an(self.resp_agent)
        an2 = self.graph_set.add_an(self.resp_agent)

        result = self.graph_set.get_an()
        self.assertEqual(len(result), 2)
        self.assertIn(an1, result)
        self.assertIn(an2, result)

    def test_get_ar(self):
        ar1 = self.graph_set.add_ar(self.resp_agent)
        ar2 = self.graph_set.add_ar(self.resp_agent)

        result = self.graph_set.get_ar()
        self.assertEqual(len(result), 2)
        self.assertIn(ar1, result)
        self.assertIn(ar2, result)

    def test_get_be(self):
        be1 = self.graph_set.add_be(self.resp_agent)
        be2 = self.graph_set.add_be(self.resp_agent)

        result = self.graph_set.get_be()
        self.assertEqual(len(result), 2)
        self.assertIn(be1, result)
        self.assertIn(be2, result)

    def test_get_br(self):
        br1 = self.graph_set.add_br(self.resp_agent)
        br2 = self.graph_set.add_br(self.resp_agent)

        result = self.graph_set.get_br()
        self.assertEqual(len(result), 2)
        self.assertIn(br1, result)
        self.assertIn(br2, result)

    def test_get_ci(self):
        ci1 = self.graph_set.add_ci(self.resp_agent)
        ci2 = self.graph_set.add_ci(self.resp_agent)

        result = self.graph_set.get_ci()
        self.assertEqual(len(result), 2)
        self.assertIn(ci1, result)
        self.assertIn(ci2, result)

    def test_get_de(self):
        de1 = self.graph_set.add_de(self.resp_agent)
        de2 = self.graph_set.add_de(self.resp_agent)

        result = self.graph_set.get_de()
        self.assertEqual(len(result), 2)
        self.assertIn(de1, result)
        self.assertIn(de2, result)

    def test_get_id(self):
        id1 = self.graph_set.add_id(self.resp_agent)
        id2 = self.graph_set.add_id(self.resp_agent)

        result = self.graph_set.get_id()
        self.assertEqual(len(result), 2)
        self.assertIn(id1, result)
        self.assertIn(id2, result)

    def test_get_pl(self):
        pl1 = self.graph_set.add_pl(self.resp_agent)
        pl2 = self.graph_set.add_pl(self.resp_agent)

        result = self.graph_set.get_pl()
        self.assertEqual(len(result), 2)
        self.assertIn(pl1, result)
        self.assertIn(pl2, result)

    def test_get_rp(self):
        rp1 = self.graph_set.add_rp(self.resp_agent)
        rp2 = self.graph_set.add_rp(self.resp_agent)

        result = self.graph_set.get_rp()
        self.assertEqual(len(result), 2)
        self.assertIn(rp1, result)
        self.assertIn(rp2, result)

    def test_get_ra(self):
        ra1 = self.graph_set.add_ra(self.resp_agent)
        ra2 = self.graph_set.add_ra(self.resp_agent)

        result = self.graph_set.get_ra()
        self.assertEqual(len(result), 2)
        self.assertIn(ra1, result)
        self.assertIn(ra2, result)

    def test_get_re(self):
        re1 = self.graph_set.add_re(self.resp_agent)
        re2 = self.graph_set.add_re(self.resp_agent)

        result = self.graph_set.get_re()
        self.assertEqual(len(result), 2)
        self.assertIn(re1, result)
        self.assertIn(re2, result)

    def test_pickle_serialization(self):
        # Create a GraphSet with some entities
        br = self.graph_set.add_br(self.resp_agent)
        br.has_title("Test Title")
        br.has_pub_date("2024-01-01")

        ar = self.graph_set.add_ar(self.resp_agent)
        ra = self.graph_set.add_ra(self.resp_agent)
        ra.has_name("Test Author")

        # Link entities
        ar.is_held_by(ra)
        br.has_contributor(ar)

        # Pickle and unpickle the GraphSet
        pickled = pickle.dumps(self.graph_set)
        restored = pickle.loads(pickled)

        # Verify state is preserved
        self.assertEqual(len(restored.res_to_entity), len(self.graph_set.res_to_entity))
        self.assertEqual(restored.base_iri, self.graph_set.base_iri)
        self.assertEqual(restored.info_dir, self.graph_set.info_dir)
        self.assertEqual(restored.supplier_prefix, self.graph_set.supplier_prefix)

        # Verify entities are accessible
        restored_br = restored.get_entity(br.res)
        self.assertIsNotNone(restored_br)
        self.assertEqual(restored_br.get_title(), "Test Title")
        self.assertEqual(restored_br.get_pub_date(), "2024-01-01")

        restored_ra = restored.get_entity(ra.res)
        self.assertIsNotNone(restored_ra)
        self.assertEqual(restored_ra.get_name(), "Test Author")

        # Verify relationships are preserved
        restored_ar = restored.get_entity(ar.res)
        self.assertIsNotNone(restored_ar)


if __name__ == '__main__':
    unittest.main()
