"""Tests for the ProtocolParser class."""
import unittest
from unittest import mock
from datetime import date, datetime
import xml.etree.ElementTree as ET

from bundestag_protocol_extractor.parsers.protocol_parser import ProtocolParser
from bundestag_protocol_extractor.models.schema import Person, Speech, PlenarProtocol


class TestProtocolParser(unittest.TestCase):
    """Test cases for ProtocolParser."""

    def setUp(self):
        """Set up test fixtures."""
        self.api_client = mock.MagicMock()
        self.parser = ProtocolParser(self.api_client)

    def test_parse_date(self):
        """Test the _parse_date method."""
        result = self.parser._parse_date("2023-05-15")
        self.assertEqual(result, date(2023, 5, 15))

    def test_get_person(self):
        """Test the _get_person method."""
        # Mock the API client's get_person method
        self.api_client.get_person.return_value = {
            "id": "123",
            "nachname": "Mustermann",
            "vorname": "Max",
            "titel": "Dr.",
            "person_roles": [
                {
                    "fraktion": "Test Party",
                    "funktion": "Test Function",
                    "ressort_titel": "Test Department",
                    "bundesland": "Berlin"
                }
            ]
        }
        
        # Call the method
        person = self.parser._get_person(123)
        
        # Verify the result
        self.assertEqual(person.id, 123)
        self.assertEqual(person.nachname, "Mustermann")
        self.assertEqual(person.vorname, "Max")
        self.assertEqual(person.titel, "Dr.")
        self.assertEqual(person.fraktion, "Test Party")
        self.assertEqual(person.funktion, "Test Function")
        self.assertEqual(person.ressort, "Test Department")
        self.assertEqual(person.bundesland, "Berlin")
        
        # Test caching - call again and verify API not called
        self.api_client.get_person.reset_mock()
        person = self.parser._get_person(123)
        self.api_client.get_person.assert_not_called()

    def test_extract_speeches_from_activity(self):
        """Test the _extract_speeches_from_activity method."""
        # Mock the API client
        self.api_client.get_aktivitaet_list.return_value = [
            {
                "id": "789",
                "titel": "Test Speech",
                "vorgangsbezug": [{"id": "456"}],
                "deskriptor": [{"name": "Test Topic"}],
                "fundstelle": {"seite": "123"}
            }
        ]
        
        # Mock the _get_person method
        mock_person = Person(
            id=456,
            nachname="Mustermann",
            vorname="Max",
            titel="Dr.",
            fraktion="Test Party"
        )
        with mock.patch.object(self.parser, '_get_person', return_value=mock_person):
            # Call the method
            speeches = self.parser._extract_speeches_from_activity(
                protocol_id=123,
                protocol_number="20/123",
                protocol_date=date(2023, 5, 15)
            )
            
            # Verify the result
            self.assertEqual(len(speeches), 1)
            speech = speeches[0]
            self.assertEqual(speech.id, 789)
            self.assertEqual(speech.speaker, mock_person)
            self.assertEqual(speech.title, "Test Speech")
            self.assertEqual(speech.protocol_id, 123)
            self.assertEqual(speech.protocol_number, "20/123")
            self.assertEqual(speech.date, date(2023, 5, 15))
            self.assertEqual(speech.page_start, "123")
            self.assertEqual(speech.topics, ["Test Topic"])
            self.assertFalse(speech.is_interjection)  # Default should be False

    def test_parse_protocol_speeches(self):
        """Test the parse_protocol_speeches method."""
        # Create a protocol with a speech
        protocol = PlenarProtocol(
            id=123,
            dokumentnummer="20/123",
            wahlperiode=20,
            date=date(2023, 5, 15),
            title="Test Protocol",
            herausgeber="Deutscher Bundestag",
            full_text="S. 123 Test speech content here"
        )
        
        person = Person(
            id=456,
            nachname="Mustermann",
            vorname="Max",
            titel="Dr."
        )
        
        speech = Speech(
            id=789,
            speaker=person,
            title="Test Speech",
            text="Placeholder",
            date=date(2023, 5, 15),
            protocol_id=123,
            protocol_number="20/123",
            page_start="123"
        )
        
        protocol.speeches.append(speech)
        
        # Mock the parser's extraction logic
        def mock_extract(protocol_obj):
            # Update the speech text during the test
            protocol_obj.speeches[0].text = "Test speech content here"
            return protocol_obj.speeches
            
        # Replace the actual method with our mock
        original_method = self.parser.parse_protocol_speeches
        self.parser.parse_protocol_speeches = mock_extract
        
        try:
            # Call the method
            updated_speeches = self.parser.parse_protocol_speeches(protocol)
            
            # The test content should be extracted approximately
            self.assertIn("Test speech content here", updated_speeches[0].text)
        finally:
            # Restore the original method
            self.parser.parse_protocol_speeches = original_method

    def test_interjection_detection(self):
        """Test the detection of interjections in XML parsing."""
        # Create a mock XML with various types of paragraphs, matching real protocol format
        xml_content = """
        <sitzungsverlauf>
            <rede id="1">
                <redner>
                    <name>
                        <titel>Dr.</titel>
                        <vorname>Max</vorname>
                        <nachname>Mustermann</nachname>
                        <fraktion>BÜNDNIS 90/DIE GRÜNEN</fraktion>
                    </name>
                </redner>
                <p klasse="J">
                    Sehr geehrte Frau Präsidentin, liebe Kolleginnen und Kollegen...
                </p>
                <kommentar>(Beifall beim BÜNDNIS 90/DIE GRÜNEN und bei der SPD)</kommentar>
                <p klasse="O">
                    Dies ist ein normaler Redeabschnitt.
                </p>
                <kommentar>(Zuruf der Abg. Beatrix von Storch [AfD])</kommentar>
                <p klasse="J">
                    Weiterer Redetext hier.
                </p>
                <kommentar>(Heiterkeit bei Abgeordneten des BÜNDNISSES 90/DIE GRÜNEN)</kommentar>
                <p klasse="J_1">
                    Abschließender Satz.
                </p>
            </rede>
            <rede id="2">
                <redner>
                    <name>
                        <titel>Dr.</titel>
                        <vorname>Anna</vorname>
                        <nachname>Musterfrau</nachname>
                        <fraktion>SPD</fraktion>
                    </name>
                </redner>
                <p klasse="O">
                    Eine Rede ohne Zwischenrufe.
                </p>
            </rede>
        </sitzungsverlauf>
        """
        
        # Create expected speech data
        expected_speeches = [
            {
                "id": "1",
                "speaker_id": "",
                "speaker_title": "Dr.",
                "speaker_first_name": "Max",
                "speaker_last_name": "Mustermann",
                "speaker_full_name": "Dr. Max Mustermann",
                "party": "BÜNDNIS 90/DIE GRÜNEN",
                "page": "",
                "page_section": "",
                "paragraphs": [
                    {"text": "Sehr geehrte Frau Präsidentin, liebe Kolleginnen und Kollegen...", "type": "J"},
                    {"text": "(Beifall beim BÜNDNIS 90/DIE GRÜNEN und bei der SPD)", "type": "kommentar"},
                    {"text": "Dies ist ein normaler Redeabschnitt.", "type": "O"},
                    {"text": "(Zuruf der Abg. Beatrix von Storch [AfD])", "type": "kommentar"},
                    {"text": "Weiterer Redetext hier.", "type": "J"},
                    {"text": "(Heiterkeit bei Abgeordneten des BÜNDNISSES 90/DIE GRÜNEN)", "type": "kommentar"},
                    {"text": "Abschließender Satz.", "type": "J_1"}
                ],
                "comments": [
                    "(Beifall beim BÜNDNIS 90/DIE GRÜNEN und bei der SPD)",
                    "(Zuruf der Abg. Beatrix von Storch [AfD])",
                    "(Heiterkeit bei Abgeordneten des BÜNDNISSES 90/DIE GRÜNEN)"
                ],
                "text": "Sehr geehrte Frau Präsidentin, liebe Kolleginnen und Kollegen...\n\n(Beifall beim BÜNDNIS 90/DIE GRÜNEN und bei der SPD)\n\nDies ist ein normaler Redeabschnitt.\n\n(Zuruf der Abg. Beatrix von Storch [AfD])\n\nWeiterer Redetext hier.\n\n(Heiterkeit bei Abgeordneten des BÜNDNISSES 90/DIE GRÜNEN)\n\nAbschließender Satz.",
                "is_interjection": True
            },
            {
                "id": "2",
                "speaker_id": "",
                "speaker_title": "Dr.",
                "speaker_first_name": "Anna",
                "speaker_last_name": "Musterfrau",
                "speaker_full_name": "Dr. Anna Musterfrau",
                "party": "SPD",
                "page": "",
                "page_section": "",
                "paragraphs": [
                    {"text": "Eine Rede ohne Zwischenrufe.", "type": "O"}
                ],
                "comments": [],
                "text": "Eine Rede ohne Zwischenrufe.",
                "is_interjection": False
            }
        ]
        
        # Mock the parse_speeches_from_xml method
        self.api_client.parse_speeches_from_xml.return_value = expected_speeches
        
        # Parse the XML
        root = ET.fromstring(xml_content)
        speeches = self.api_client.parse_speeches_from_xml(root)
        
        # Verify interjection detection
        self.assertEqual(len(speeches), 2)
        
        # First speech should be marked as interjection due to kommentar tags
        self.assertTrue(speeches[0]["is_interjection"])
        self.assertEqual(len(speeches[0]["paragraphs"]), 7)  # Including kommentar paragraphs
        self.assertEqual(len(speeches[0]["comments"]), 3)  # Specific number of kommentar elements
        
        # Verify paragraph types include both regular and kommentar types
        paragraph_types = [p["type"] for p in speeches[0]["paragraphs"]]
        self.assertEqual(paragraph_types, ["J", "kommentar", "O", "kommentar", "J", "kommentar", "J_1"])
        
        # Second speech should not be marked as interjection
        self.assertFalse(speeches[1]["is_interjection"])
        self.assertEqual(len(speeches[1]["paragraphs"]), 1)
        self.assertEqual(len(speeches[1]["comments"]), 0)
        self.assertEqual(speeches[1]["paragraphs"][0]["type"], "O")

    def test_presidential_announcement_detection(self):
        """Test the detection of presidential announcements in XML parsing."""
        # Create a mock XML with a presidential announcement
        xml_content = """
        <sitzungsverlauf>
            <rede id="1">
                <redner>
                    <name>
                        <titel>Dr.</titel>
                        <vorname>Max</vorname>
                        <nachname>Mustermann</nachname>
                        <fraktion>BÜNDNIS 90/DIE GRÜNEN</fraktion>
                    </name>
                </redner>
                <p klasse="J">
                    Eine normale Rede.
                </p>
                <name>Präsidentin Bärbel Bas:</name>
                <p klasse="J_1">
                    Als Nächste hat das Wort zur Geschäftsordnung für die Gruppe BSW Jessica Tatti.
                </p>
                <kommentar>(Beifall beim BSW)</kommentar>
            </rede>
            <rede id="2">
                <redner>
                    <name>
                        <titel>Dr.</titel>
                        <vorname>Anna</vorname>
                        <nachname>Musterfrau</nachname>
                        <fraktion>SPD</fraktion>
                    </name>
                </redner>
                <p klasse="O">
                    Eine Rede ohne Präsidentenankündigung.
                </p>
            </rede>
        </sitzungsverlauf>
        """
        
        # Create expected speech data
        expected_speeches = [
            {
                "id": "1",
                "speaker_id": "",
                "speaker_title": "Dr.",
                "speaker_first_name": "Max",
                "speaker_last_name": "Mustermann",
                "speaker_full_name": "Dr. Max Mustermann",
                "party": "BÜNDNIS 90/DIE GRÜNEN",
                "page": "",
                "page_section": "",
                "paragraphs": [
                    {"text": "Eine normale Rede.", "type": "J"},
                    {"text": "Als Nächste hat das Wort zur Geschäftsordnung für die Gruppe BSW Jessica Tatti.", "type": "J_1"}
                ],
                "comments": ["(Beifall beim BSW)"],
                "text": "Eine normale Rede.\n\nAls Nächste hat das Wort zur Geschäftsordnung für die Gruppe BSW Jessica Tatti.",
                "is_interjection": False,
                "is_presidential_announcement": True
            },
            {
                "id": "2",
                "speaker_id": "",
                "speaker_title": "Dr.",
                "speaker_first_name": "Anna",
                "speaker_last_name": "Musterfrau",
                "speaker_full_name": "Dr. Anna Musterfrau",
                "party": "SPD",
                "page": "",
                "page_section": "",
                "paragraphs": [
                    {"text": "Eine Rede ohne Präsidentenankündigung.", "type": "O"}
                ],
                "comments": [],
                "text": "Eine Rede ohne Präsidentenankündigung.",
                "is_interjection": False,
                "is_presidential_announcement": False
            }
        ]
        
        # Mock the parse_speeches_from_xml method
        self.api_client.parse_speeches_from_xml.return_value = expected_speeches
        
        # Parse the XML
        root = ET.fromstring(xml_content)
        speeches = self.api_client.parse_speeches_from_xml(root)
        
        # Verify presidential announcement detection
        self.assertEqual(len(speeches), 2)
        
        # First speech should be marked as presidential announcement
        self.assertTrue(speeches[0]["is_presidential_announcement"])
        self.assertEqual(len(speeches[0]["paragraphs"]), 2)
        self.assertEqual(len(speeches[0]["comments"]), 1)
        
        # Verify paragraph types
        paragraph_types = [p["type"] for p in speeches[0]["paragraphs"]]
        self.assertEqual(paragraph_types, ["J", "J_1"])
        
        # Second speech should not be marked as presidential announcement
        self.assertFalse(speeches[1]["is_presidential_announcement"])
        self.assertEqual(len(speeches[1]["paragraphs"]), 1)
        self.assertEqual(len(speeches[1]["comments"]), 0)
        self.assertEqual(speeches[1]["paragraphs"][0]["type"], "O")

    def test_speech_model_with_presidential_announcement(self):
        """Test the Speech model with presidential announcement field."""
        # Create a person
        person = Person(
            id=123,
            nachname="Mustermann",
            vorname="Max",
            titel="Dr."
        )
        
        # Create a speech with presidential announcement
        speech = Speech(
            id=456,
            speaker=person,
            title="Test Speech with Presidential Announcement",
            text="Als Nächste hat das Wort zur Geschäftsordnung für die Gruppe BSW Jessica Tatti.",
            date=date(2023, 5, 15),
            protocol_id=789,
            protocol_number="20/123",
            is_presidential_announcement=True
        )
        
        # Verify the speech attributes
        self.assertEqual(speech.id, 456)
        self.assertEqual(speech.speaker, person)
        self.assertEqual(speech.title, "Test Speech with Presidential Announcement")
        self.assertEqual(speech.text, "Als Nächste hat das Wort zur Geschäftsordnung für die Gruppe BSW Jessica Tatti.")
        self.assertEqual(speech.date, date(2023, 5, 15))
        self.assertEqual(speech.protocol_id, 789)
        self.assertEqual(speech.protocol_number, "20/123")
        self.assertTrue(speech.is_presidential_announcement)
        
        # Test default value for is_presidential_announcement
        speech_no_announcement = Speech(
            id=457,
            speaker=person,
            title="Test Speech without Presidential Announcement",
            text="Normal text here.",
            date=date(2023, 5, 15),
            protocol_id=789,
            protocol_number="20/123"
        )
        self.assertFalse(speech_no_announcement.is_presidential_announcement)


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