//! Vue SFC 编译器
//!
//! 将 .vue 文件的 template 编译为生成 h() 调用的 render 函数，
//! 与 iris-js 注入的 Vue 运行时兼容。

use regex::Regex;

/// Vue SFC 编译结果
pub struct CompiledVueSFC {
    pub name: String,
    /// Render 函数 (JavaScript) — 调用 h() 生成 VNode
    pub render_fn: String,
    /// CSS 样式
    pub styles: Vec<String>,
}

/// Vue SFC 编译器
pub struct VueSFCCompiler;

impl VueSFCCompiler {
    /// 编译 Vue SFC 文件
    pub fn compile(vue_source: &str) -> Result<CompiledVueSFC, String> {
        let template = Self::extract_section(vue_source, "template")?;
        let script = Self::extract_section(vue_source, "script")?;
        let styles: Vec<String> = Self::extract_all_sections(vue_source, "style");

        // 生成 h() 调用代码
        let render_body = Self::compile_template(&template);
        let render_fn = format!("function render() {{ return {}; }}", render_body);

        let name = Self::extract_component_name(&script).unwrap_or_else(|| "Anonymous".to_string());

        Ok(CompiledVueSFC { name, render_fn, styles })
    }

    /// 将 HTML template 编译为 h() 调用表达式
    fn compile_template(template: &str) -> String {
        let template = template.trim();
        if template.is_empty() {
            return "null".to_string();
        }

        // 查找第一个根元素
        Self::compile_node(template)
    }

    /// 编译单个 HTML 节点为 h(tag, props, children) 调用
    fn compile_node(html: &str) -> String {
        let html = html.trim();
        if html.is_empty() {
            return "null".to_string();
        }

        // 文本节点（不包含 < 字符的纯文本）
        if !html.starts_with('<') {
            return Self::compile_text_content(html);
        }

        // 提取标签名
        let tag_name = match Self::extract_tag_name(html) {
            Some(t) => t,
            None => return Self::compile_text_content(html),
        };

        // 提取属性
        let attrs = Self::extract_attributes(html);

        // 提取子节点内容（标签内的内容）
        let closing = format!("</{}>", tag_name);
        let content_start = match html.find('>') {
            Some(pos) => pos + 1,
            None => return format!("h('{}', {})", tag_name, attrs),
        };
        let content_end = match html[content_start..].find(&closing) {
            Some(pos) => content_start + pos,
            None => html.len(),
        };
        let inner = &html[content_start..content_end];

        // 编译子节点
        let children = Self::compile_children(inner);

        if children == "null" {
            format!("h('{}', {})", tag_name, attrs)
        } else {
            format!("h('{}', {}, {})", tag_name, attrs, children)
        }
    }

    /// 编译文本内容（支持 {{ expr }} 插值）
    fn compile_text_content(text: &str) -> String {
        let text = text.trim();
        if text.is_empty() {
            return "null".to_string();
        }

        // 查找插值表达式 {{ expr }}
        let re = Regex::new(r"\{\{\s*([^}]+)\s*\}\}").unwrap();
        let mut parts = Vec::new();
        let mut last_end = 0;

        for cap in re.captures_iter(text) {
            let match_start = cap.get(0).unwrap().start();
            let match_end = cap.get(0).unwrap().end();

            // 插值前的纯文本
            if match_start > last_end {
                let literal = &text[last_end..match_start];
                parts.push(format!("'{}'", literal.replace('\'', "\\'")));
            }

            // 插值表达式（运行时求值）
            let expr = cap.get(1).unwrap().as_str().trim();
            parts.push(format!("({})", expr));

            last_end = match_end;
        }

        // 剩余文本
        if last_end < text.len() {
            let literal = &text[last_end..];
            parts.push(format!("'{}'", literal.replace('\'', "\\'")));
        }

        match parts.len() {
            0 => "null".to_string(),
            1 => format!("text({})", parts[0]),
            _ => {
                // 多个片段拼接
                let concat = parts.join(" + ");
                format!("text({})", concat)
            }
        }
    }

    /// 编译子节点列表
    fn compile_children(inner: &str) -> String {
        let inner = inner.trim();
        if inner.is_empty() {
            return "null".to_string();
        }

        let nodes = Self::split_child_nodes(inner);
        if nodes.is_empty() {
            return "null".to_string();
        }

        let compiled: Vec<String> = nodes.iter().map(|n| Self::compile_node(n)).collect();
        let joined = compiled.join(", ");
        format!("[{}]", joined)
    }

    /// 将 HTML 文本分割为顶层子节点列表
    fn split_child_nodes(html: &str) -> Vec<String> {
        let mut nodes = Vec::new();
        let mut remaining = html.trim();

        while !remaining.is_empty() {
            if remaining.starts_with('<') {
                // 元素节点：找到匹配的结束标签
                let tag_name = match Self::extract_tag_name(remaining) {
                    Some(t) => t,
                    None => { remaining = &remaining[1..]; continue; }
                };
                let closing = format!("</{}>", tag_name);
                let end_tag_pos = Self::find_closing_tag(remaining, &tag_name, &closing);
                let node_end = end_tag_pos + closing.len();
                nodes.push(remaining[..node_end].to_string());
                remaining = remaining[node_end..].trim();
            } else {
                // 文本节点：找到下一个 < 或结尾
                let next_tag = remaining.find('<').unwrap_or(remaining.len());
                let text = remaining[..next_tag].trim().to_string();
                if !text.is_empty() {
                    nodes.push(text);
                }
                remaining = remaining[next_tag..].trim();
            }
        }

        nodes
    }

    /// 找到匹配的结束标签位置（处理嵌套）
    fn find_closing_tag(html: &str, tag_name: &str, closing: &str) -> usize {
        let mut depth = 1i32;
        let mut pos = closing.len(); // 从第一个子节点之后开始
        let open_tag = format!("<{}", tag_name);

        while pos < html.len() && depth > 0 {
            let remaining = &html[pos..];
            if let Some(open_pos) = remaining.find(&open_tag) {
                if let Some(close_pos) = remaining.find(closing) {
                    if open_pos < close_pos {
                        depth += 1;
                        pos += open_pos + 1;
                    } else {
                        depth -= 1;
                        if depth == 0 {
                            return pos + close_pos;
                        }
                        pos += close_pos + 1;
                    }
                } else {
                    break;
                }
            } else if let Some(close_pos) = remaining.find(closing) {
                depth -= 1;
                if depth == 0 {
                    return pos + close_pos;
                }
                pos += close_pos + 1;
            } else {
                break;
            }
        }
        html.len()
    }

    /// 提取标签名
    fn extract_tag_name(html: &str) -> Option<String> {
        let re = Regex::new(r#"^<\s*(\w+(?:-\w+)*)"#).ok()?;
        re.captures(html).map(|c| c[1].to_string())
    }

    /// 提取标签属性为 JS 对象字面量
    fn extract_attributes(html: &str) -> String {
        // 提取标签头内的所有属性
        let tag_end = html.find('>').unwrap_or(html.len());
        let tag_section = &html[1..tag_end]; // 跳过 <

        // 去掉标签名，剩下属性部分
        let after_tag = Self::skip_tag_name(tag_section);
        let attr_str = after_tag.trim();

        if attr_str.is_empty() {
            return "{}".to_string();
        }

        // 解析 key="value" 和 :key="value" 属性
        let re = Regex::new(r#"(\:?\w+(?:-\w+)*)(?:\s*=\s*"([^"]*)")?"#).unwrap();
        let mut props = Vec::new();

        for cap in re.captures_iter(attr_str) {
            let name = cap[1].to_string();
            let value = cap.get(2).map(|m| m.as_str()).unwrap_or("");

            if name.starts_with(':') {
                // :bind 表达式
                let prop_name = &name[1..];
                props.push(format!("{}:({})", prop_name, value));
            } else if !value.is_empty() {
                // 静态属性
                props.push(format!("{}:'{}'", name, value.replace('\'', "\\'")));
            }
        }

        if props.is_empty() {
            "{}".to_string()
        } else {
            format!("{{{}}}", props.join(","))
        }
    }

    /// 跳过 HTML 标签头部的标签名
    fn skip_tag_name(s: &str) -> &str {
        let s = s.trim();
        for (i, c) in s.char_indices() {
            if c.is_whitespace() {
                return &s[i..];
            }
            // 如果标签名后直接是 > 或 /
            if c == '>' || c == '/' {
                return &s[i..];
            }
        }
        ""
    }

    /// 提取 section
    fn extract_section(source: &str, tag: &str) -> Result<String, String> {
        let start_tag = format!("<{}", tag);
        let end_tag = format!("</{}>", tag);

        let start = source.find(&start_tag)
            .ok_or_else(|| format!("Missing <{}> tag", tag))?;

        let content_start = source[start..].find('>')
            .map(|i| start + i + 1)
            .ok_or_else(|| format!("Invalid <{}> tag", tag))?;

        let end = source.find(&end_tag)
            .ok_or_else(|| format!("Missing </{}> tag", tag))?;

        Ok(source[content_start..end].to_string())
    }

    fn extract_all_sections(source: &str, tag: &str) -> Vec<String> {
        let mut results = Vec::new();
        let mut remaining = source;

        while let Ok(section) = Self::extract_section(remaining, tag) {
            results.push(section);
            if let Some(pos) = remaining.find(&format!("</{}>", tag)) {
                remaining = &remaining[pos + format!("</{}>", tag).len()..];
            } else {
                break;
            }
        }

        results
    }

    fn extract_component_name(script: &str) -> Option<String> {
        let re = Regex::new(r#"name:\s*['"](\w+)['"]"#).ok()?;
        re.captures(script).map(|cap| cap[1].to_string())
    }
}

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

    #[test]
    fn test_compile_simple_element() {
        let result = VueSFCCompiler::compile_template("<div>hello</div>");
        assert_eq!(result, "h('div', {}, [text('hello')])");
    }

#[test]
    fn test_compile_with_interpolation() {
        let result = VueSFCCompiler::compile_template("<h1>{{ title }}</h1>");
        assert_eq!(result, "h('h1', {}, [text((title))])");
    }

#[test]
    fn test_compile_nested() {
        let result = VueSFCCompiler::compile_template("<div><p>text</p></div>");
        assert_eq!(result, "h('div', {}, [h('p', {}, [text('text')])])");
    }

    #[test]
    fn test_compile_with_attrs() {
        let result = VueSFCCompiler::compile_template(r#"<div class="foo" id="bar">x</div>"#);
        assert!(result.contains("class:'foo'"));
        assert!(result.contains("id:'bar'"));
    }

    #[test]
    fn test_compile_with_bind() {
        let result = VueSFCCompiler::compile_template(r#"<div :class="cls">x</div>"#);
        assert!(result.contains("class:(cls)"));
    }

#[test]
    fn test_compile_full_sfc() {
        let vue = r#"<template><div>Count: {{ count }}</div></template><script setup>const count = ref(0)</script>"#;
        let compiled = VueSFCCompiler::compile(vue).unwrap();
        assert!(compiled.render_fn.contains("h"));
        assert!(compiled.render_fn.contains("count"));
    }

#[test]
    fn test_mixed_text_and_interpolation() {
        let result = VueSFCCompiler::compile_template("<p>Hello {{ name }}!</p>");
        assert!(result.contains("h('p'"));
        assert!(result.contains("(name)"));
    }
}