@file:Suppress("HardCodedStringLiteral")

package eu.gaudian.translator.model.grammar

import org.junit.Test
import org.junit.Assert.*
import org.junit.Before

/**
 * Unit tests for DictionaryJsonParser.
 * 
 * These tests verify that the JSON parser correctly extracts and structures
 * dictionary data from various JSON formats, ensuring robust handling of
 * different dictionary data structures.
 */
class DictionaryJsonParserTest {

    private lateinit var parser: DictionaryJsonParser

    @Before
    fun setUp() {
        parser = DictionaryJsonParser
    }

    @Test
    fun `parseJson with valid complete dictionary entry returns structured data`() {
        // Given
        val json = """
        {
            "translations": [
                {
                    "lang_code": "en",
                    "word": "house",
                    "sense": "building"
                },
                {
                    "language_code": "fr", 
                    "word": "maison",
                    "sense": "dwelling"
                }
            ],
            "relations": {
                "synonyms": [
                    {"word": "home", "sense_index": "1"},
                    {"word": "residence"}
                ],
                "hyponyms": [
                    {"word": "cottage"}
                ]
            },
            "phonetics": {
                "ipa": ["[haʊs]", "[haʊz]"]
            },
            "hyphenation": ["house"],
            "etymology": {
                "texts": ["From Old English hūs.", "Related to German Haus."]
            },
            "senses": [
                {
                    "gloss": "A building for human habitation.",
                    "topics": ["architecture", "building"],
                    "examples": ["They live in a small house."]
                }
            ],
            "grammatical_properties": {
                "other_tags": ["noun", "countable"]
            },
            "pronunciation": [
                {
                    "ipa": "/haʊs/",
                    "rhymes": "mouse"
                }
            ],
            "audio_files": [
                {
                    "url": "https://example.com/house.mp3",
                    "description": "Pronunciation of house"
                }
            ],
            "inflections": [
                {
                    "form": "houses",
                    "grammatical_features": ["plural"]
                }
            ]
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        
        // Test translations
        assertEquals(2, result?.translations?.size)
        val enTranslation = result?.translations?.find { it.languageCode == "en" }
        assertEquals("house", enTranslation?.word)
        assertEquals("building", enTranslation?.sense)
        
        val frTranslation = result?.translations?.find { it.languageCode == "fr" }
        assertEquals("maison", frTranslation?.word)
        assertEquals("dwelling", frTranslation?.sense)
        
        // Test relations
        assertEquals(2, result?.relations?.size)
        val synonyms = result?.synonyms
        assertEquals(2, synonyms?.size)
        assertTrue(synonyms?.any { it.word == "home" } == true)
        assertTrue(synonyms?.any { it.word == "residence" } == true)
        
        val hyponyms = result?.hyponyms
        assertEquals(1, hyponyms?.size)
        assertEquals("cottage", hyponyms?.first()?.word)
        
        // Test phonetics
        assertEquals(2, result?.phonetics?.size)
        assertTrue(result?.phonetics?.contains("haʊs") == true)
        assertTrue(result?.phonetics?.contains("haʊz") == true)
        
        // Test hyphenation
        assertEquals(1, result?.hyphenation?.size)
        assertEquals("house", result?.hyphenation?.first())
        
        // Test etymology
        assertEquals(2, result?.etymology?.texts?.size)
        assertTrue(result?.etymology?.texts?.contains("From Old English hūs.") == true)
        
        // Test senses
        assertEquals(1, result?.senses?.size)
        val sense = result?.senses?.first()
        assertEquals("A building for human habitation.", sense?.gloss)
        assertTrue(sense?.topics?.contains("architecture") == true)
        assertTrue(sense?.examples?.contains("They live in a small house.") == true)
        
        // Test grammatical properties
        assertNotNull(result?.grammaticalProperties)
        assertTrue(result?.grammaticalProperties?.otherTags?.contains("noun") == true)
        
        // Test pronunciation
        assertEquals(1, result?.pronunciation?.size)
        val pronunciation = result?.pronunciation?.first()
        assertEquals("/haʊs/", pronunciation?.ipa)
        assertEquals("mouse", pronunciation?.rhymes)
        
        // Test audio files
        assertEquals(1, result?.audioFiles?.size)
        val audio = result?.audioFiles?.first()
        assertEquals("https://example.com/house.mp3", audio?.url)
        assertEquals("Pronunciation of house", audio?.description)
        
        // Test inflections
        assertEquals(1, result?.inflections?.size)
        val inflection = result?.inflections?.first()
        assertEquals("houses", inflection?.form)
        assertTrue(inflection?.grammaticalFeatures?.contains("plural") == true)
    }

    @Test
    fun `parseJson with minimal dictionary entry returns data with empty collections`() {
        // Given
        val json = """{}"""

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(0, result?.translations?.size)
        assertEquals(0, result?.relations?.size)
        assertEquals(0, result?.phonetics?.size)
        assertEquals(0, result?.hyphenation?.size)
        assertEquals(0, result?.etymology?.texts?.size)
        assertEquals(0, result?.senses?.size)
        assertNull(result?.grammaticalProperties)
        assertEquals(0, result?.pronunciation?.size)
        assertEquals(0, result?.audioFiles?.size)
        assertEquals(0, result?.inflections?.size)
    }

    @Test
    fun `parseJson with etymology as single text returns correct structure`() {
        // Given
        val json = """
        {
            "etymology": {
                "text": "Single etymology text."
            }
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(1, result?.etymology?.texts?.size)
        assertEquals("Single etymology text.", result?.etymology?.texts?.first())
    }

    @Test
    fun `parseJson with etymology as array returns correct structure`() {
        // Given
        val json = """
        {
            "etymology": [
                "First etymology text.",
                "Second etymology text."
            ]
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(2, result?.etymology?.texts?.size)
        assertTrue(result?.etymology?.texts?.contains("First etymology text.") == true)
        assertTrue(result?.etymology?.texts?.contains("Second etymology text.") == true)
    }

    @Test
    fun `parseJson with invalid JSON returns null`() {
        // Given
        val invalidJson = """{ invalid json }"""

        // When
        val result = parser.parseJson(invalidJson)

        // Then
        assertNull(result)
    }

    @Test
    fun `parseJson with translations using language_code field works correctly`() {
        // Given
        val json = """
        {
            "translations": [
                {
                    "language_code": "de",
                    "word": "Haus"
                }
            ]
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(1, result?.translations?.size)
        val translation = result?.translations?.first()
        assertEquals("de", translation?.languageCode)
        assertEquals("Haus", translation?.word)
    }

    @Test
    fun `parseJson with translations missing required fields filters them out`() {
        // Given
        val json = """
        {
            "translations": [
                {
                    "lang_code": "en",
                    "word": "house"
                },
                {
                    "lang_code": "fr"
                    // Missing word field
                },
                {
                    "word": "casa"
                    // Missing lang_code field
                }
            ]
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(1, result?.translations?.size)
        assertEquals("en", result?.translations?.first()?.languageCode)
        assertEquals("house", result?.translations?.first()?.word)
    }

    @Test
    fun `parseJson with relations missing word field filters them out`() {
        // Given
        val json = """
        {
            "relations": {
                "synonyms": [
                    {"word": "home"},
                    {"sense_index": "1"}
                    // Missing word field
                ]
            }
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(1, result?.synonyms?.size)
        assertEquals("home", result?.synonyms?.first()?.word)
    }

    @Test
    fun `parseJson handles phonetics with brackets correctly`() {
        // Given
        val json = """
        {
            "phonetics": {
                "ipa": ["[haʊs]", "[haʊz]"]
            }
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(2, result?.phonetics?.size)
        assertTrue(result?.phonetics?.contains("haʊs") == true) // Brackets should be removed
        assertTrue(result?.phonetics?.contains("haʊz") == true)
    }

    @Test
    fun `parseJson with empty arrays returns empty collections`() {
        // Given
        val json = """
        {
            "translations": [],
            "relations": {},
            "phonetics": {"ipa": []},
            "hyphenation": [],
            "etymology": {"texts": []},
            "senses": [],
            "pronunciation": [],
            "audio_files": [],
            "inflections": []
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(0, result?.translations?.size)
        assertEquals(0, result?.relations?.size)
        assertEquals(0, result?.phonetics?.size)
        assertEquals(0, result?.hyphenation?.size)
        assertEquals(0, result?.etymology?.texts?.size)
        assertEquals(0, result?.senses?.size)
        assertEquals(0, result?.pronunciation?.size)
        assertEquals(0, result?.audioFiles?.size)
        assertEquals(0, result?.inflections?.size)
    }

    @Test
    fun `parseJson with senses missing gloss filters them out`() {
        // Given
        val json = """
        {
            "senses": [
                {
                    "gloss": "Valid definition",
                    "topics": ["topic1"],
                    "examples": ["example1"]
                },
                {
                    "topics": ["topic2"]
                    // Missing gloss field
                }
            ]
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(1, result?.senses?.size)
        assertEquals("Valid definition", result?.senses?.first()?.gloss)
    }

    @Test
    fun `parseJson with pronunciation missing ipa filters them out`() {
        // Given
        val json = """
        {
            "pronunciation": [
                {
                    "ipa": "/haʊs/",
                    "rhymes": "mouse"
                },
                {
                    "rhymes": "house"
                    // Missing ipa field
                }
            ]
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(1, result?.pronunciation?.size)
        assertEquals("/haʊs/", result?.pronunciation?.first()?.ipa)
    }

    @Test
    fun `parseJson with audio files missing url filters them out`() {
        // Given
        val json = """
        {
            "audio_files": [
                {
                    "url": "https://example.com/audio.mp3",
                    "description": "Audio description"
                },
                {
                    "description": "Missing URL"
                    // Missing url field
                }
            ]
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(1, result?.audioFiles?.size)
        assertEquals("https://example.com/audio.mp3", result?.audioFiles?.first()?.url)
    }

    @Test
    fun `parseJson with inflections missing form filters them out`() {
        // Given
        val json = """
        {
            "inflections": [
                {
                    "form": "houses",
                    "grammatical_features": ["plural"]
                },
                {
                    "grammatical_features": ["singular"]
                    // Missing form field
                }
            ]
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        assertEquals(1, result?.inflections?.size)
        assertEquals("houses", result?.inflections?.first()?.form)
    }

    @Test
    fun `DictionaryEntryData convenience properties work correctly`() {
        // Given
        val json = """
        {
            "relations": {
                "synonyms": [{"word": "home"}, {"word": "residence"}],
                "hyponyms": [{"word": "cottage"}],
                "hypernyms": [{"word": "building"}]
            }
        }
        """.trimIndent()

        // When
        val result = parser.parseJson(json)

        // Then
        assertNotNull(result)
        
        // Test synonyms convenience property
        assertEquals(2, result?.synonyms?.size)
        assertTrue(result?.synonyms?.all { it.relationType == "synonyms" } == true)
        
        // Test hyponyms convenience property
        assertEquals(1, result?.hyponyms?.size)
        assertTrue(result?.hyponyms?.all { it.relationType == "hyponyms" } == true)
        
        // Test allRelatedWords convenience property
        assertEquals(3, result?.allRelatedWords?.size)
        assertTrue(result?.allRelatedWords?.any { it.word == "home" } == true)
        assertTrue(result?.allRelatedWords?.any { it.word == "cottage" } == true)
        assertTrue(result?.allRelatedWords?.any { it.word == "building" } == true)
    }
}
