import unittest

from semantic_index.models import IndexDocument
from semantic_index.redmine import RedmineMapper


class RedmineMapperTest(unittest.TestCase):
    def test_issue_chunks_have_stable_ids_and_metadata(self):
        issue = {
            "id": 42,
            "subject": "Widget order ORD-12345 cannot ship",
            "description": "Customer reports that widget order ORD-12345 is blocked.",
            "project": {"id": 7, "identifier": "fud-helpdesk"},
            "contact": {"id": 9, "email": "ada@example.com", "name": "Ada Lovelace"},
            "created_on": "2026-04-01T10:00:00Z",
            "updated_on": "2026-04-02T10:00:00Z",
            "url": "http://redmine.local/issues/42",
        }

        first = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)
        second = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)

        self.assertEqual([doc.id for doc in first], [doc.id for doc in second])
        self.assertEqual("redmine:issue:42:chunk:0", first[0].id)
        self.assertEqual("issue", first[0].payload["doc_type"])
        self.assertEqual(42, first[0].payload["issue_id"])
        self.assertEqual("fud-helpdesk", first[0].payload["project_identifier"])
        self.assertIsNone(first[0].payload["project_name"])
        self.assertFalse(first[0].payload["has_helpdesk_ticket"])
        self.assertEqual("ada@example.com", first[0].payload["contact_email"])
        self.assertEqual("Ada Lovelace", first[0].payload["contact_name"])
        self.assertEqual("http://redmine.local/issues/42", first[0].payload["redmine_url"])
        self.assertIn("source_hash", first[0].payload)

    def test_helpdesk_ticket_contact_is_mapped_to_all_issue_chunks(self):
        issue = {
            "id": 39779,
            "subject": "Goods return",
            "description": "Please arrange to return these goods.",
            "project": {"id": 1, "identifier": "customer-service"},
            "helpdesk_ticket": {
                "id": 35159,
                "contact_id": 1890,
                "from_address": "callum@safetagtracking.com",
                "contact": {
                    "id": 1890,
                    "name": "Callum Mackeonis",
                    "company": "SafeTag Tracking",
                    "email": "callum@safetagtracking.com",
                },
            },
            "journals": [
                {"id": 71570, "notes": "Hello, yes we can arrange this today.", "created_on": "2026-04-14T14:29:49Z"}
            ],
        }

        docs = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)
        issue_doc = next(doc for doc in docs if doc.payload["doc_type"] == "issue")
        journal_doc = next(doc for doc in docs if doc.payload["doc_type"] == "journal")
        contact_doc = next(doc for doc in docs if doc.payload["doc_type"] == "contact")

        for doc in (issue_doc, journal_doc, contact_doc):
            self.assertEqual(35159, doc.payload["helpdesk_ticket_id"])
            self.assertTrue(doc.payload["has_helpdesk_ticket"])
            self.assertEqual(1890, doc.payload["contact_id"])
            self.assertEqual("Callum Mackeonis", doc.payload["contact_name"])
            self.assertEqual("SafeTag Tracking", doc.payload["contact_company"])
            self.assertEqual("callum@safetagtracking.com", doc.payload["contact_email"])
        self.assertIn("Callum Mackeonis", issue_doc.text)
        self.assertIn("callum@safetagtracking.com", contact_doc.text)

    def test_configured_project_identifier_is_used_when_issue_payload_omits_identifier(self):
        issue = {
            "id": 42,
            "subject": "Widget order",
            "description": "Body",
            "project": {"id": 1, "name": "Customer Service"},
        }

        docs = RedmineMapper(
            redmine_url="http://redmine.local",
            project_identifier="customer-service",
        ).issue_to_documents(issue)

        self.assertEqual("customer-service", docs[0].payload["project_identifier"])
        self.assertEqual("Customer Service", docs[0].payload["project_name"])

    def test_internal_non_helpdesk_issue_keeps_project_metadata_without_contact(self):
        issue = {
            "id": 55,
            "subject": "Internal hiring task",
            "description": "Follow up with candidate.",
            "project": {"id": 68, "identifier": "hiring", "name": "Hiring"},
        }

        docs = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)

        self.assertEqual(1, len(docs))
        self.assertEqual("hiring", docs[0].payload["project_identifier"])
        self.assertEqual("Hiring", docs[0].payload["project_name"])
        self.assertFalse(docs[0].payload["has_helpdesk_ticket"])
        self.assertIsNone(docs[0].payload["contact_id"])

    def test_issue_journals_messages_and_contact_are_mapped(self):
        issue = {
            "id": 42,
            "subject": "Widget order",
            "description": "Ticket envelope",
            "project": {"id": 7, "identifier": "fud-helpdesk"},
            "contact": {"id": 9, "email": "ada@example.com", "name": "Ada Lovelace"},
            "journals": [
                {"id": 5, "notes": "Private escalation note", "private_notes": True, "created_on": "2026-04-03T10:00:00Z"}
            ],
            "messages": [
                {"id": 6, "body": "Customer reply body", "direction": "incoming", "created_on": "2026-04-03T11:00:00Z"}
            ],
        }

        docs = RedmineMapper(redmine_url="http://redmine.local").issue_to_documents(issue)
        ids = {doc.id for doc in docs}
        types = {doc.payload["doc_type"] for doc in docs}

        self.assertIn("redmine:issue:42:journal:5:chunk:0", ids)
        self.assertIn("redmine:issue:42:message:6:chunk:0", ids)
        self.assertIn("redmine:contact:9:issue:42:chunk:0", ids)
        self.assertEqual({"issue", "journal", "message", "contact"}, types)
        journal = next(doc for doc in docs if doc.payload["doc_type"] == "journal")
        message = next(doc for doc in docs if doc.payload["doc_type"] == "message")
        self.assertEqual("private", journal.payload["visibility"])
        self.assertEqual("incoming", message.payload["direction"])

    def test_empty_documents_are_rejected(self):
        with self.assertRaises(ValueError):
            IndexDocument(id="x", text=" ", payload={})


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