use crate::error::Result;
use crate::format::{self, FormatOptions, Formatter};
use crate::model::{Lang, TwineFile};
use crate::output::OutputProcessor;

pub struct AndroidFormatter;

impl Formatter for AndroidFormatter {
    fn name(&self) -> &'static str {
        "android"
    }
    fn extension(&self) -> &'static str {
        ".xml"
    }
    fn default_file_name(&self) -> &'static str {
        "strings.xml"
    }
    fn can_handle_directory(&self, path: &str) -> bool {
        std::fs::read_dir(path).map_or(false, |entries| {
            entries.filter_map(|e| e.ok()).any(|e| {
                e.file_name()
                    .to_str()
                    .map_or(false, |n| n.starts_with("values"))
            })
        })
    }
    fn detect_language(&self, path: &str) -> Option<String> {
        let re = regex::Regex::new(r"^values-([a-z]{2}(-r[a-z]{2})?)$").unwrap();
        for segment in path.split('/') {
            if segment == "values" {
                return None;
            }
            if let Some(caps) = re.captures(segment) {
                return Some(caps[1].replace("-r", "-"));
            }
        }
        None
    }
    fn output_dir_for_lang(&self, lang: &str) -> String {
        if lang.is_empty() {
            "values".to_string()
        } else {
            let re = regex::Regex::new(r"-(\p{Lu})").unwrap();
            format!("values-{}", re.replace_all(lang, "-r$1"))
        }
    }

    fn read(&self, content: &str, lang: &str, twine_file: &mut TwineFile) -> Result<()> {
        let string_re = regex::Regex::new(r#"<string name="([^"]+)">(.*?)</string>"#).unwrap();
        let comment_re = regex::Regex::new(r"<!--\s*(.*?)\s*-->").unwrap();
        let mut last_comment: Option<String> = None;

        for line in content.lines() {
            if let Some(caps) = comment_re.captures(line) {
                let c = caps[1].trim().to_string();
                if !c.is_empty() && !c.starts_with("SECTION:") {
                    last_comment = Some(c);
                } else {
                    last_comment = None;
                }
                continue;
            }
            if let Some(caps) = string_re.captures(line) {
                let key = caps[1].to_string();
                let mut value = html_unescape(&caps[2]);
                value = value.replace("\\'", "'").replace("\\\"", "\"");
                value = crate::placeholders::convert_placeholders_from_android_to_twine(&value);
                value = value.replace("\\@", "@");
                format::set_translation(twine_file, &key, lang, &value);
                if let Some(ref c) = last_comment {
                    format::set_comment(twine_file, &key, c);
                }
                last_comment = None;
            }
        }
        Ok(())
    }

    fn format(&self, lang: &str, twine_file: &TwineFile, options: &FormatOptions) -> Result<Option<String>> {
        let processor = OutputProcessor::new(twine_file.clone(), options.clone());
        let processed = processor.process(lang);

        let mut out = format!(
            "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\
             <!-- Android Strings File -->\n\
             <!-- Generated by Twinex -->\n\
             <!-- Language: {} -->\n\
             <resources>\n",
            lang
        );
        let mut has_content = false;

        for section in &processed.sections {
            if section.definitions.is_empty() {
                continue;
            }
            if !section.name.as_str().is_empty() {
                out.push_str(&format!("\t<!-- SECTION: {} -->\n", section.name));
            }
            for def in &section.definitions {
                if let Some(ref comment) = def.comment {
                    out.push_str(&format!("\t<!-- {} -->\n", comment.replace("--", "—")));
                }
                let value = def.translations.get(&Lang::new(lang)).map(|s| s.as_str()).unwrap_or("");
                let escaped = xml_escape(value);
                out.push_str(&format!("\t<string name=\"{}\">{}</string>\n", def.key, escaped));
                has_content = true;
            }
        }
        if has_content {
            out.push_str("</resources>\n");
            Ok(Some(out))
        } else {
            Ok(None)
        }
    }
}

fn html_unescape(s: &str) -> String {
    s.replace("&lt;", "<")
        .replace("&gt;", ">")
        .replace("&amp;", "&")
        .replace("&quot;", "\"")
        .replace("&apos;", "'")
        .replace("\\'", "'")
}

fn xml_escape(s: &str) -> String {
    s.replace('&', "&amp;")
        .replace('<', "&lt;")
        .replace('>', "&gt;")
        .replace('"', "&quot;")
        .replace('\'', "&apos;")
}