//! Morphology parsing for Greek/Hebrew parsing codes (Tyndale/Robinson style).
//!
//! Parses strings like "V-PAP-DSM" into structured morphological data.

use serde::Serialize;
use std::str::FromStr;
use thiserror::Error;

#[derive(Debug, Error, PartialEq, Eq)]
pub enum ParsingError {
    #[error("Empty parsing code")]
    Empty,
    #[error("Unknown part of speech: {0}")]
    UnknownPartOfSpeech(String),
    #[error("Unknown tense: {0}")]
    UnknownTense(char),
    #[error("Unknown voice: {0}")]
    UnknownVoice(char),
    #[error("Unknown mood: {0}")]
    UnknownMood(char),
    #[error("Unknown case: {0}")]
    UnknownCase(char),
    #[error("Unknown number: {0}")]
    UnknownNumber(char),
    #[error("Unknown gender: {0}")]
    UnknownGender(char),
    #[error("Unknown person: {0}")]
    UnknownPerson(char),
    #[error("Invalid verb code: {0}")]
    InvalidVerbCode(String),
    #[error("Invalid inflection code: {0}")]
    InvalidInflectionCode(String),
}

/// Part of speech categories.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum PartOfSpeech {
    Noun,
    Verb,
    Adjective,
    Adverb,
    Article,
    Conjunction,
    Conditional,
    Interjection,
    Particle,
    Preposition,
    Pronoun,
    Demonstrative,
    Correlative,
    Interrogative,
    Indefinite,
    Personal,
    Relative,
    Reflexive,
    Reciprocal,
    Possessive,
    Aramaic,
}

/// Grammatical case.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum Case {
    Nominative,
    Genitive,
    Dative,
    Accusative,
    Vocative,
}

/// Grammatical number.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum Number {
    Singular,
    Plural,
}

/// Grammatical gender.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum Gender {
    Masculine,
    Feminine,
    Neuter,
}

/// Verb tense.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum Tense {
    Present,
    Imperfect,
    Future,
    Aorist,
    Perfect,
    Pluperfect,
    Second,
}

/// Verb voice.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum Voice {
    Active,
    Middle,
    Passive,
    MiddleOrPassive,
    MiddleDeponent,
    PassiveDeponent,
    MiddleOrPassiveDeponent,
    Impersonal,
}

/// Verb mood.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum Mood {
    Indicative,
    Subjunctive,
    Optative,
    Imperative,
    Infinitive,
    Participle,
}

/// Person for verb conjugation.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum Person {
    First,
    Second,
    Third,
}

/// A fully parsed morphology structure.
///
/// Not all fields apply to all parts of speech:
/// - Verbs use tense, voice, mood, and optionally person/case/number/gender (for participles)
/// - Nouns/Adjectives use case, number, gender
/// - Some parts of speech (conjunctions, prepositions) may have no inflections
#[derive(Debug, Clone, PartialEq, Eq, Default, Serialize)]
#[serde(rename_all = "snake_case")]
pub struct Parsing {
    pub part_of_speech: Option<PartOfSpeech>,
    pub case: Option<Case>,
    pub number: Option<Number>,
    pub gender: Option<Gender>,
    pub tense: Option<Tense>,
    pub voice: Option<Voice>,
    pub mood: Option<Mood>,
    pub person: Option<Person>,
}

impl Parsing {
    /// Creates a new Parsing with just a part of speech.
    pub fn new(part_of_speech: PartOfSpeech) -> Self {
        Self {
            part_of_speech: Some(part_of_speech),
            ..Default::default()
        }
    }

    /// Builder method to set case.
    pub fn with_case(mut self, case: Case) -> Self {
        self.case = Some(case);
        self
    }

    /// Builder method to set number.
    pub fn with_number(mut self, number: Number) -> Self {
        self.number = Some(number);
        self
    }

    /// Builder method to set gender.
    pub fn with_gender(mut self, gender: Gender) -> Self {
        self.gender = Some(gender);
        self
    }

    /// Builder method to set tense.
    pub fn with_tense(mut self, tense: Tense) -> Self {
        self.tense = Some(tense);
        self
    }

    /// Builder method to set voice.
    pub fn with_voice(mut self, voice: Voice) -> Self {
        self.voice = Some(voice);
        self
    }

    /// Builder method to set mood.
    pub fn with_mood(mut self, mood: Mood) -> Self {
        self.mood = Some(mood);
        self
    }

    /// Builder method to set person.
    pub fn with_person(mut self, person: Person) -> Self {
        self.person = Some(person);
        self
    }

    pub fn to_json_string(&self) -> String {
        serde_json::to_string(self).unwrap_or_default()
    }

    pub fn to_json_value(&self) -> serde_json::Value {
        serde_json::to_value(self).unwrap_or_default()
    }
}

pub fn parse_to_json(code: &str) -> Result<String, ParsingError> {
    let parsing: Parsing = code.parse()?;
    Ok(parsing.to_json_string())
}

fn parse_case(c: char) -> Result<Case, ParsingError> {
    match c {
        'N' => Ok(Case::Nominative),
        'G' => Ok(Case::Genitive),
        'D' => Ok(Case::Dative),
        'A' => Ok(Case::Accusative),
        'V' => Ok(Case::Vocative),
        _ => Err(ParsingError::UnknownCase(c)),
    }
}

fn parse_number(c: char) -> Result<Number, ParsingError> {
    match c {
        'S' => Ok(Number::Singular),
        'P' => Ok(Number::Plural),
        _ => Err(ParsingError::UnknownNumber(c)),
    }
}

fn parse_gender(c: char) -> Result<Gender, ParsingError> {
    match c {
        'M' => Ok(Gender::Masculine),
        'F' => Ok(Gender::Feminine),
        'N' => Ok(Gender::Neuter),
        _ => Err(ParsingError::UnknownGender(c)),
    }
}

fn parse_tense(c: char) -> Result<Tense, ParsingError> {
    match c {
        'P' => Ok(Tense::Present),
        'I' => Ok(Tense::Imperfect),
        'F' => Ok(Tense::Future),
        'A' => Ok(Tense::Aorist),
        'X' | 'R' => Ok(Tense::Perfect),
        'Y' | 'L' => Ok(Tense::Pluperfect),
        '2' => Ok(Tense::Second),
        _ => Err(ParsingError::UnknownTense(c)),
    }
}

fn parse_voice(c: char) -> Result<Voice, ParsingError> {
    match c {
        'A' => Ok(Voice::Active),
        'M' => Ok(Voice::Middle),
        'P' => Ok(Voice::Passive),
        'E' => Ok(Voice::MiddleOrPassive),
        'D' => Ok(Voice::MiddleDeponent),
        'O' => Ok(Voice::PassiveDeponent),
        'N' => Ok(Voice::MiddleOrPassiveDeponent),
        'Q' => Ok(Voice::Impersonal),
        'X' => Ok(Voice::MiddleOrPassive),
        _ => Err(ParsingError::UnknownVoice(c)),
    }
}

fn parse_mood(c: char) -> Result<Mood, ParsingError> {
    match c {
        'I' => Ok(Mood::Indicative),
        'S' => Ok(Mood::Subjunctive),
        'O' => Ok(Mood::Optative),
        'M' => Ok(Mood::Imperative),
        'N' => Ok(Mood::Infinitive),
        'P' => Ok(Mood::Participle),
        _ => Err(ParsingError::UnknownMood(c)),
    }
}

fn parse_person(c: char) -> Result<Person, ParsingError> {
    match c {
        '1' => Ok(Person::First),
        '2' => Ok(Person::Second),
        '3' => Ok(Person::Third),
        _ => Err(ParsingError::UnknownPerson(c)),
    }
}

fn parse_case_number_gender(code: &str) -> Result<(Case, Number, Gender), ParsingError> {
    let chars: Vec<char> = code.chars().collect();
    if chars.len() != 3 {
        return Err(ParsingError::InvalidInflectionCode(code.to_string()));
    }
    Ok((
        parse_case(chars[0])?,
        parse_number(chars[1])?,
        parse_gender(chars[2])?,
    ))
}

fn parse_person_case_number_gender(
    code: &str,
) -> Result<(Person, Case, Number, Gender), ParsingError> {
    let chars: Vec<char> = code.chars().collect();
    if chars.len() != 4 {
        return Err(ParsingError::InvalidInflectionCode(code.to_string()));
    }
    Ok((
        parse_person(chars[0])?,
        parse_case(chars[1])?,
        parse_number(chars[2])?,
        parse_gender(chars[3])?,
    ))
}

fn parse_person_adjnum_case_number_gender(
    code: &str,
) -> Result<(Person, Case, Number, Gender), ParsingError> {
    let chars: Vec<char> = code.chars().collect();
    if chars.len() != 5 {
        return Err(ParsingError::InvalidInflectionCode(code.to_string()));
    }
    Ok((
        parse_person(chars[0])?,
        parse_case(chars[2])?,
        parse_number(chars[3])?,
        parse_gender(chars[4])?,
    ))
}

fn parse_person_case_number(code: &str) -> Result<(Person, Case, Number), ParsingError> {
    let chars: Vec<char> = code.chars().collect();
    if chars.len() != 3 {
        return Err(ParsingError::InvalidInflectionCode(code.to_string()));
    }
    Ok((
        parse_person(chars[0])?,
        parse_case(chars[1])?,
        parse_number(chars[2])?,
    ))
}

impl FromStr for Parsing {
    type Err = ParsingError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let s = s.trim();
        if s.is_empty() {
            return Err(ParsingError::Empty);
        }

        let parts: Vec<&str> = s.split('-').collect();
        if parts.is_empty() {
            return Err(ParsingError::Empty);
        }

        let pos_code = parts[0];

        match pos_code {
            "CONJ" => return Ok(Parsing::new(PartOfSpeech::Conjunction)),
            "COND" => return Ok(Parsing::new(PartOfSpeech::Conjunction)),
            "PREP" => return Ok(Parsing::new(PartOfSpeech::Preposition)),
            "ADV" => return Ok(Parsing::new(PartOfSpeech::Adverb)),
            "PRT" => return Ok(Parsing::new(PartOfSpeech::Particle)),
            "INJ" => return Ok(Parsing::new(PartOfSpeech::Interjection)),
            "ARAM" => return Ok(Parsing::new(PartOfSpeech::Aramaic)),
            "NUI" => return Ok(Parsing::new(PartOfSpeech::Adjective)),
            _ => {}
        }

        let pos = match pos_code {
            "N" => PartOfSpeech::Noun,
            "V" => PartOfSpeech::Verb,
            "A" => PartOfSpeech::Adjective,
            "T" => PartOfSpeech::Article,
            "P" => PartOfSpeech::Pronoun,
            "D" => PartOfSpeech::Demonstrative,
            "K" => PartOfSpeech::Correlative,
            "I" => PartOfSpeech::Interrogative,
            "X" => PartOfSpeech::Indefinite,
            "F" => PartOfSpeech::Reflexive,
            "R" => PartOfSpeech::Relative,
            "S" => PartOfSpeech::Possessive,
            "C" => PartOfSpeech::Reciprocal,
            "Q" => PartOfSpeech::Correlative,
            _ => return Err(ParsingError::UnknownPartOfSpeech(pos_code.to_string())),
        };

        let mut parsing = Parsing::new(pos);

        if pos == PartOfSpeech::Verb {
            if parts.len() < 2 {
                return Err(ParsingError::InvalidVerbCode(s.to_string()));
            }

            let verb_code = parts[1];
            let verb_chars: Vec<char> = verb_code.chars().collect();

            let (tense_offset, tense) = if verb_chars.first() == Some(&'2') {
                (1, Tense::Second)
            } else {
                (0, parse_tense(verb_chars[0])?)
            };

            let min_len = 3 + tense_offset;
            if verb_chars.len() < min_len {
                return Err(ParsingError::InvalidVerbCode(s.to_string()));
            }

            parsing.tense = Some(tense);
            parsing.voice = Some(parse_voice(verb_chars[tense_offset + 1])?);
            parsing.mood = Some(parse_mood(verb_chars[tense_offset + 2])?);

            if verb_chars.len() > min_len {
                parsing.person = Some(parse_person(verb_chars[min_len])?);
            }

            if parts.len() >= 3 {
                let inflection = parts[2];
                let inflection_chars: Vec<char> = inflection.chars().collect();
                if inflection_chars.len() >= 3
                    && parse_case(inflection_chars[0]).is_ok()
                    && parse_number(inflection_chars[1]).is_ok()
                    && parse_gender(inflection_chars[2]).is_ok()
                {
                    parsing.case = Some(parse_case(inflection_chars[0])?);
                    parsing.number = Some(parse_number(inflection_chars[1])?);
                    parsing.gender = Some(parse_gender(inflection_chars[2])?);
                } else if inflection_chars.len() == 2
                    && parse_person(inflection_chars[0]).is_ok()
                    && parse_number(inflection_chars[1]).is_ok()
                {
                    parsing.person = Some(parse_person(inflection_chars[0])?);
                    parsing.number = Some(parse_number(inflection_chars[1])?);
                }
            }
        } else if pos == PartOfSpeech::Reflexive && parts.len() >= 2 {
            let code = parts[1];
            let chars: Vec<char> = code.chars().collect();
            if chars.len() == 4 && chars[0].is_ascii_digit() {
                let (person, case, number, gender) = parse_person_case_number_gender(code)?;
                parsing.person = Some(person);
                parsing.case = Some(case);
                parsing.number = Some(number);
                parsing.gender = Some(gender);
            } else if chars.len() == 3 {
                let (case, number, gender) = parse_case_number_gender(code)?;
                parsing.case = Some(case);
                parsing.number = Some(number);
                parsing.gender = Some(gender);
            }
        } else if pos == PartOfSpeech::Possessive && parts.len() >= 2 {
            let code = parts[1];
            let chars: Vec<char> = code.chars().collect();
            if chars.len() == 5 && chars[0].is_ascii_digit() {
                let (person, case, number, gender) = parse_person_adjnum_case_number_gender(code)?;
                parsing.person = Some(person);
                parsing.case = Some(case);
                parsing.number = Some(number);
                parsing.gender = Some(gender);
            } else if chars.len() == 3 {
                let (case, number, gender) = parse_case_number_gender(code)?;
                parsing.case = Some(case);
                parsing.number = Some(number);
                parsing.gender = Some(gender);
            }
        } else if pos == PartOfSpeech::Pronoun && parts.len() >= 2 {
            let code = parts[1];
            let chars: Vec<char> = code.chars().collect();
            if chars.len() == 3 && chars[0].is_ascii_digit() {
                let (person, case, number) = parse_person_case_number(code)?;
                parsing.person = Some(person);
                parsing.case = Some(case);
                parsing.number = Some(number);
            } else if chars.len() == 3 {
                let (case, number, gender) = parse_case_number_gender(code)?;
                parsing.case = Some(case);
                parsing.number = Some(number);
                parsing.gender = Some(gender);
            }
        } else if pos == PartOfSpeech::Noun && parts.len() >= 2 {
            let code = parts[1];
            match code {
                "LI" | "OI" | "PRI" => {}
                _ => {
                    let chars: Vec<char> = code.chars().collect();
                    if chars.len() >= 3 {
                        parsing.case = Some(parse_case(chars[0])?);
                        parsing.number = Some(parse_number(chars[1])?);
                        parsing.gender = Some(parse_gender(chars[2])?);
                    }
                }
            }
        } else if pos == PartOfSpeech::Adjective && parts.len() >= 2 {
            let code = parts[1];
            if code == "NUI" {
            } else {
                let chars: Vec<char> = code.chars().collect();
                if chars.len() >= 3 {
                    parsing.case = Some(parse_case(chars[0])?);
                    parsing.number = Some(parse_number(chars[1])?);
                    parsing.gender = Some(parse_gender(chars[2])?);
                }
            }
        } else if parts.len() >= 2 {
            let code = parts[1];
            let chars: Vec<char> = code.chars().collect();
            if chars.len() >= 3 {
                if let (Ok(case), Ok(number), Ok(gender)) = (
                    parse_case(chars[0]),
                    parse_number(chars[1]),
                    parse_gender(chars[2]),
                ) {
                    parsing.case = Some(case);
                    parsing.number = Some(number);
                    parsing.gender = Some(gender);
                }
            }
        }

        Ok(parsing)
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_cond() {
        let parsing: Parsing = "COND".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Conjunction));
        assert_eq!(parsing.case, None);
    }

    #[test]
    fn test_adjective_apm() {
        let parsing: Parsing = "A-APM".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Adjective));
        assert_eq!(parsing.case, Some(Case::Accusative));
        assert_eq!(parsing.number, Some(Number::Plural));
        assert_eq!(parsing.gender, Some(Gender::Masculine));
    }

    #[test]
    fn test_verb_participle_pap_dsm() {
        let parsing: Parsing = "V-PAP-DSM".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Verb));
        assert_eq!(parsing.tense, Some(Tense::Present));
        assert_eq!(parsing.voice, Some(Voice::Active));
        assert_eq!(parsing.mood, Some(Mood::Participle));
        assert_eq!(parsing.case, Some(Case::Dative));
        assert_eq!(parsing.number, Some(Number::Singular));
        assert_eq!(parsing.gender, Some(Gender::Masculine));
    }

    #[test]
    fn test_demonstrative_nsm() {
        let parsing: Parsing = "D-NSM".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Demonstrative));
        assert_eq!(parsing.case, Some(Case::Nominative));
        assert_eq!(parsing.number, Some(Number::Singular));
        assert_eq!(parsing.gender, Some(Gender::Masculine));
    }

    #[test]
    fn test_pronoun_nsm() {
        let parsing: Parsing = "P-NSM".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Pronoun));
        assert_eq!(parsing.case, Some(Case::Nominative));
        assert_eq!(parsing.number, Some(Number::Singular));
        assert_eq!(parsing.gender, Some(Gender::Masculine));
    }

    #[test]
    fn test_invalid_empty() {
        let result: Result<Parsing, _> = "".parse();
        assert_eq!(result, Err(ParsingError::Empty));
    }

    #[test]
    fn test_invalid_unknown_pos() {
        let result: Result<Parsing, _> = "Z-NSM".parse();
        assert!(matches!(result, Err(ParsingError::UnknownPartOfSpeech(_))));
    }

    #[test]
    fn test_noun_gsf() {
        let parsing: Parsing = "N-GSF".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Noun));
        assert_eq!(parsing.case, Some(Case::Genitive));
        assert_eq!(parsing.number, Some(Number::Singular));
        assert_eq!(parsing.gender, Some(Gender::Feminine));
    }

    #[test]
    fn test_verb_indicative() {
        let parsing: Parsing = "V-PAI".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Verb));
        assert_eq!(parsing.tense, Some(Tense::Present));
        assert_eq!(parsing.voice, Some(Voice::Active));
        assert_eq!(parsing.mood, Some(Mood::Indicative));
        assert_eq!(parsing.case, None);
    }

    #[test]
    fn test_personal_pronoun_1as() {
        let parsing: Parsing = "P-1AS".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Pronoun));
        assert_eq!(parsing.person, Some(Person::First));
        assert_eq!(parsing.case, Some(Case::Accusative));
        assert_eq!(parsing.number, Some(Number::Singular));
    }

    #[test]
    fn test_reflexive_3asm() {
        let parsing: Parsing = "F-3ASM".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Reflexive));
        assert_eq!(parsing.person, Some(Person::Third));
        assert_eq!(parsing.case, Some(Case::Accusative));
        assert_eq!(parsing.number, Some(Number::Singular));
        assert_eq!(parsing.gender, Some(Gender::Masculine));
    }

    #[test]
    fn test_possessive_1pasf() {
        let parsing: Parsing = "S-1PASF".parse().unwrap();
        assert_eq!(parsing.part_of_speech, Some(PartOfSpeech::Possessive));
        assert_eq!(parsing.person, Some(Person::First));
        assert_eq!(parsing.case, Some(Case::Accusative));
        assert_eq!(parsing.number, Some(Number::Singular));
        assert_eq!(parsing.gender, Some(Gender::Feminine));
    }

    #[test]
    fn test_to_json_value() {
        let parsing: Parsing = "V-PAP-DSM".parse().unwrap();
        let value = parsing.to_json_value();

        assert!(value.is_object());
        let obj = value.as_object().unwrap();

        assert_eq!(
            obj.get("part_of_speech").and_then(|v| v.as_str()),
            Some("Verb")
        );
        assert_eq!(obj.get("tense").and_then(|v| v.as_str()), Some("Present"));
        assert_eq!(obj.get("voice").and_then(|v| v.as_str()), Some("Active"));
        assert_eq!(obj.get("mood").and_then(|v| v.as_str()), Some("Participle"));
        assert_eq!(obj.get("case").and_then(|v| v.as_str()), Some("Dative"));
        assert_eq!(obj.get("number").and_then(|v| v.as_str()), Some("Singular"));
        assert_eq!(
            obj.get("gender").and_then(|v| v.as_str()),
            Some("Masculine")
        );
    }

    #[test]
    fn test_to_json_value_noun() {
        let parsing: Parsing = "N-GSF".parse().unwrap();
        let value = parsing.to_json_value();

        assert!(value.is_object());
        let obj = value.as_object().unwrap();

        assert_eq!(
            obj.get("part_of_speech").and_then(|v| v.as_str()),
            Some("Noun")
        );
        assert_eq!(obj.get("case").and_then(|v| v.as_str()), Some("Genitive"));
        assert_eq!(obj.get("number").and_then(|v| v.as_str()), Some("Singular"));
        assert_eq!(obj.get("gender").and_then(|v| v.as_str()), Some("Feminine"));
        assert!(obj.get("tense").and_then(|v| v.as_str()).is_none());
    }
}
