use std::path::Path;

use crate::{
    api::search::kanji::Kanji,
    jotoba::{
        language::Language,
        words::{
            dialect::Dialect, field::Field, misc::Misc, part_of_speech::PartOfSpeech,
            pitch::PitchPart,
        },
    },
};

use serde::{Deserialize, Serialize};

/// The API response struct for a word search
#[derive(Serialize, Deserialize)]
pub struct Response {
    kanji: Vec<Kanji>,
    words: Vec<Word>,
}

impl Response {
    pub fn new(words: Vec<Word>, kanji: Vec<Kanji>) -> Self {
        Self { kanji, words }
    }

    #[cfg(feature = "jotoba_intern")]
    pub fn from<P: AsRef<Path>>(
        wres: (
            Vec<&crate::jotoba::words::Word>,
            Vec<&crate::jotoba::kanji::Kanji>,
        ),
        assets_path: P,
    ) -> Self {
        let kanji = convert_kanji(wres.1, assets_path);
        let words = convert_words(wres.0);

        Self { kanji, words }
    }
}

/// Represents a single Word result with 1 (main) Japanese reading and n glosses
#[derive(Serialize, Deserialize)]
pub struct Word {
    reading: Reading,
    common: bool,
    senses: Vec<Sense>,
    #[serde(skip_serializing_if = "Option::is_none")]
    alt_readings: Option<Vec<Reading>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    audio: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pitch: Option<Vec<PitchPart>>,
}

#[derive(Serialize, Deserialize)]
pub struct Reading {
    kana: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    kanji: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    furigana: Option<String>,
}

#[derive(Serialize, Deserialize)]
pub struct Sense {
    glosses: Vec<String>,
    pos: Vec<PartOfSpeech>,
    language: Language,
    #[serde(skip_serializing_if = "Option::is_none")]
    dialect: Option<Dialect>,
    #[serde(skip_serializing_if = "Option::is_none")]
    field: Option<Field>,
    #[serde(skip_serializing_if = "Option::is_none")]
    information: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    antonym: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    misc: Option<Misc>,
    #[serde(skip_serializing_if = "Option::is_none")]
    xref: Option<String>,
}

impl From<&crate::jotoba::words::sense::Sense> for Sense {
    fn from(sense: &crate::jotoba::words::sense::Sense) -> Self {
        let pos = sense.part_of_speech.clone();

        let glosses = sense
            .glosses
            .iter()
            .map(|i| i.gloss.clone())
            .collect::<Vec<_>>();

        Self {
            glosses,
            pos,
            language: sense.language,
            dialect: sense.dialect,
            field: sense.field,
            information: sense.information.as_ref().cloned(),
            antonym: sense.antonym.as_ref().cloned(),
            misc: sense.misc,
            xref: sense.xref.as_ref().cloned(),
        }
    }
}

#[cfg(feature = "jotoba_intern")]
impl From<&crate::jotoba::words::Word> for Word {
    #[inline]
    fn from(word: &crate::jotoba::words::Word) -> Self {
        let kanji = word.reading.kanji.as_ref().map(|i| i.reading.clone());
        let kana = word.reading.kana.clone().reading;
        let furigana = word.furigana.clone();

        let senses = word.senses.iter().map(|i| Sense::from(i)).collect();

        let pitch = word.get_first_pitch().map(|i| i.parts.clone());

        Self {
            common: word.is_common(),
            reading: Reading {
                kanji,
                kana,
                furigana,
            },
            senses,
            alt_readings: None,
            audio: word.audio_file_name(),
            pitch,
        }
    }
}

#[cfg(feature = "jotoba_intern")]
#[inline]
fn convert_kanji<P: AsRef<std::path::Path>>(
    wres: Vec<&crate::jotoba::kanji::Kanji>,
    assets_path: P,
) -> Vec<Kanji> {
    wres.into_iter()
        .map(|i| Kanji::from(i, assets_path.as_ref()))
        .collect()
}

#[cfg(feature = "jotoba_intern")]
#[inline]
fn convert_words(wres: Vec<&crate::jotoba::words::Word>) -> Vec<Word> {
    wres.into_iter().map(|i| i.into()).collect()
}
