use aurora_core::AuroraResult;

pub trait TtsEngine: Send {
    fn speak(&self, text: &str, lang: &str) -> AuroraResult<()>;
}

pub struct NoopTts;

impl TtsEngine for NoopTts {
    fn speak(&self, _text: &str, _lang: &str) -> AuroraResult<()> {
        Ok(())
    }
}

#[cfg(feature = "tts-online")]
pub mod edge {
    use super::TtsEngine;
    use aurora_core::{AuroraResult, AuroraError};
    use std::process::{Command, Stdio};

    pub struct EdgeTts;

    impl EdgeTts {
        fn lang_code(lang: &str) -> &str {
            if lang.starts_with("ru") {
                "ru-RU"
            } else {
                "en-US"
            }
        }

        fn voice_name(lang: &str) -> &str {
            if lang.starts_with("ru") {
                "ru-RU-DariyaNeural"
            } else {
                "en-US-JennyNeural"
            }
        }
    }

    impl TtsEngine for EdgeTts {
        fn speak(&self, text: &str, lang: &str) -> AuroraResult<()> {
            let voice = Self::voice_name(lang);

            let ssml = format!(
                r#"<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis" xml:lang="{}">
                    <voice name="{}">{}</voice>
                </speak>"#,
                Self::lang_code(lang),
                voice,
                text.replace('&', "&amp;")
                    .replace('<', "&lt;")
                    .replace('>', "&gt;")
                    .replace('"', "&quot;")
                    .replace('\'', "&apos;")
            );

            let output = Command::new("curl")
                .args([
                    "-s",
                    "-X", "POST",
                    "https://eastus.tts.speech.microsoft.com/cognitiveservices/v1",
                    "-H", "Content-Type: application/ssml+xml",
                    "-H", "X-Microsoft-OutputFormat: audio-16khz-32kbitrate-mono-mp3",
                    "-H", "User-Agent: Aurora",
                    "-d", &ssml,
                    "--output", "-",
                ])
                .stdout(Stdio::piped())
                .stderr(Stdio::null())
                .output()
                .map_err(|e| AuroraError::TtsError(format!("Failed to call Edge TTS: {e}")))?;

            if output.status.success() && !output.stdout.is_empty() {
                play_audio_data(&output.stdout)?;
            }

            Ok(())
        }
    }

    fn play_audio_data(data: &[u8]) -> AuroraResult<()> {
        use std::io::Write;
        let tmp = std::env::temp_dir().join("aurora_tts.mp3");
        let mut file = std::fs::File::create(&tmp)
            .map_err(|e| AuroraError::TtsError(format!("Cannot create temp file: {e}")))?;
        file.write_all(data)
            .map_err(|e| AuroraError::TtsError(format!("Cannot write audio: {e}")))?;

        for player in &["ffplay", "mpg123", "mpv", "aplay", "paplay"] {
            if Command::new(player)
                .arg(&tmp)
                .stdout(Stdio::null())
                .stderr(Stdio::null())
                .status()
                .is_ok()
            {
                break;
            }
        }

        std::fs::remove_file(&tmp).ok();
        Ok(())
    }
}
