//! 代码生成模块
//!
//! 提供 build.rs 中使用的工具函数：
//! - `MsgCodeGen`：将透明类型的 C 头文件和 Python 存根写入目标目录
//! - `OpaqueCodeGen`：为不透明数据生成 C++ / Python 用户文件模板
//! - `NodeCodeGen`：为多语言节点生成带通讯端口的基类和用户文件

use std::fs;
use std::path::PathBuf;

// ═══════════════════════════════════════════════════════════════════
// MsgCodeGen — 透明类型
// ═══════════════════════════════════════════════════════════════════

/// 消息代码生成器
///
/// 在 build.rs 中用于将 `#[roplat_msg]` 生成的头文件和存根写入到文件系统。
/// 透明类型的文件由 roplat 管理，每次构建都会覆盖。
pub struct MsgCodeGen {
    cpp_output_dir: PathBuf,
    python_output_dir: PathBuf,
}

impl MsgCodeGen {
    pub fn new(cpp_output_dir: impl Into<PathBuf>, python_output_dir: impl Into<PathBuf>) -> Self {
        Self {
            cpp_output_dir: cpp_output_dir.into(),
            python_output_dir: python_output_dir.into(),
        }
    }

    /// 写入 C 头文件
    pub fn write_c_header(&self, struct_name: &str, content: &str) -> std::io::Result<()> {
        fs::create_dir_all(&self.cpp_output_dir)?;
        let path = self
            .cpp_output_dir
            .join(format!("{}.h", to_snake_case(struct_name)));
        fs::write(&path, content)?;
        println!("cargo:warning=Generated C header: {}", path.display());
        Ok(())
    }

    /// 写入 Python ctypes 存根
    pub fn write_python_stub(&self, struct_name: &str, content: &str) -> std::io::Result<()> {
        fs::create_dir_all(&self.python_output_dir)?;
        let path = self
            .python_output_dir
            .join(format!("{}.py", to_snake_case(struct_name)));
        fs::write(&path, content)?;
        println!("cargo:warning=Generated Python stub: {}", path.display());
        Ok(())
    }

    /// 同时写入 C 头文件和 Python 存根
    pub fn write_all(
        &self,
        struct_name: &str,
        c_header: &str,
        python_stub: &str,
    ) -> std::io::Result<()> {
        self.write_c_header(struct_name, c_header)?;
        self.write_python_stub(struct_name, python_stub)?;
        Ok(())
    }
}

// ═══════════════════════════════════════════════════════════════════
// OpaqueCodeGen — 不透明数据
// ═══════════════════════════════════════════════════════════════════

/// 不透明数据代码生成器
///
/// 为不透明数据类型生成 C++ 和 Python 中的用户侧实现模板。
///
/// - 基类文件（`*_base.h`, `*_base.py`）由 roplat 管理，每次构建覆盖
/// - 用户文件（`*.h`, `*.py`）仅在不存在时首次生成
pub struct OpaqueCodeGen {
    base_cpp_dir: PathBuf,
    base_py_dir: PathBuf,
    user_cpp_dir: PathBuf,
    user_py_dir: PathBuf,
}

impl OpaqueCodeGen {
    /// 创建生成器，基类和用户文件输出到同一目录
    pub fn new(cpp_dir: impl Into<PathBuf>, py_dir: impl Into<PathBuf>) -> Self {
        let cpp = cpp_dir.into();
        let py = py_dir.into();
        Self {
            base_cpp_dir: cpp.clone(),
            base_py_dir: py.clone(),
            user_cpp_dir: cpp,
            user_py_dir: py,
        }
    }

    /// 创建生成器，基类和用户文件输出到不同目录
    pub fn with_user_dirs(
        base_cpp_dir: impl Into<PathBuf>,
        base_py_dir: impl Into<PathBuf>,
        user_cpp_dir: impl Into<PathBuf>,
        user_py_dir: impl Into<PathBuf>,
    ) -> Self {
        Self {
            base_cpp_dir: base_cpp_dir.into(),
            base_py_dir: base_py_dir.into(),
            user_cpp_dir: user_cpp_dir.into(),
            user_py_dir: user_py_dir.into(),
        }
    }

    /// 生成 C++ 不透明类型的基类头文件（由 roplat 管理，持续更新）
    pub fn write_cpp_base_header(
        &self,
        struct_name: &str,
        fields: &[OpaqueField],
    ) -> std::io::Result<()> {
        fs::create_dir_all(&self.base_cpp_dir)?;

        let guard = format!("ROPLAT_OPAQUE_{}_BASE_H", struct_name.to_uppercase());
        let snake = to_snake_case(struct_name);
        let base_class = format!("{}Base", struct_name);

        let mut header = String::new();
        header.push_str(&format!("#ifndef {guard}\n#define {guard}\n\n"));
        header.push_str("#include <cstdint>\n#include <cstddef>\n\n");

        // FFI 函数声明
        header.push_str("extern \"C\" {\n");
        header.push_str(&format!("    void* roplat_opaque_{snake}_create();\n"));
        header.push_str(&format!(
            "    void roplat_opaque_{snake}_destroy(void* ptr);\n"
        ));
        header.push_str(&format!("    size_t roplat_opaque_{snake}_size();\n"));
        header.push_str("}\n\n");

        // 基类定义
        header.push_str(&format!("class {base_class} {{\n"));
        header.push_str("protected:\n");
        for field in fields {
            header.push_str(&format!("    {} {} = {{}};\n", field.cpp_type, field.name));
        }

        header.push_str("\npublic:\n");
        header.push_str(&format!("    virtual ~{base_class}() = default;\n"));

        for field in fields {
            let cap = capitalize(&field.name);
            header.push_str(&format!(
                "    {} get{cap}() const {{ return {}; }}\n",
                field.cpp_type, field.name
            ));
            header.push_str(&format!(
                "    void set{cap}({} value) {{ {} = value; }}\n",
                field.cpp_type, field.name
            ));
        }

        header.push_str("};\n\n");
        header.push_str(&format!("#endif // {guard}\n"));

        let path = self.base_cpp_dir.join(format!("{snake}_base.h"));
        fs::write(&path, &header)?;
        println!(
            "cargo:warning=Generated C++ opaque base: {}",
            path.display()
        );
        Ok(())
    }

    /// 生成 C++ 不透明类型的用户实现文件（仅当不存在时生成）
    pub fn write_cpp_user_file(&self, struct_name: &str) -> std::io::Result<()> {
        fs::create_dir_all(&self.user_cpp_dir)?;

        let snake = to_snake_case(struct_name);
        let path = self.user_cpp_dir.join(format!("{snake}.h"));

        if path.exists() {
            println!(
                "cargo:warning=C++ user file exists, skipping: {}",
                path.display()
            );
            return Ok(());
        }

        let base_class = format!("{}Base", struct_name);
        let guard = format!("ROPLAT_OPAQUE_{}_H", struct_name.to_uppercase());
        let base_include = make_relative_include(
            &self.user_cpp_dir,
            &self.base_cpp_dir,
            &format!("{snake}_base.h"),
        );

        let mut content = String::new();
        content.push_str(&format!("#ifndef {guard}\n#define {guard}\n\n"));
        content.push_str(&format!("#include \"{base_include}\"\n\n"));
        content.push_str(&format!("/// User-defined opaque type: {struct_name}\n"));
        content.push_str(&format!(
            "/// Inherit from {base_class} and implement your logic\n"
        ));
        content.push_str(&format!(
            "class {struct_name} : public {base_class} {{\npublic:\n"
        ));
        content.push_str(&format!("    {struct_name}() = default;\n"));
        content.push_str(&format!("    ~{struct_name}() override = default;\n\n"));
        content.push_str("    // TODO: Add your custom methods and data here\n");
        content.push_str("};\n\n");
        content.push_str(&format!("#endif // {guard}\n"));

        fs::write(&path, &content)?;
        println!("cargo:warning=Generated C++ user file: {}", path.display());
        Ok(())
    }

    /// 生成 Python 不透明类型的基类文件（由 roplat 管理，持续更新）
    pub fn write_python_base(
        &self,
        struct_name: &str,
        fields: &[OpaqueField],
    ) -> std::io::Result<()> {
        fs::create_dir_all(&self.base_py_dir)?;

        let snake = to_snake_case(struct_name);
        let base_class = format!("{}Base", struct_name);

        let mut content = String::new();
        content.push_str("# Auto-generated by roplat - DO NOT EDIT\n\n\n");

        content.push_str(&format!("class {base_class}:\n"));
        content.push_str("    \"\"\"Base class for opaque type, managed by roplat.\"\"\"\n\n");

        content.push_str("    def __init__(self):\n");
        for field in fields {
            content.push_str(&format!(
                "        self.{}: {} = {}\n",
                field.name, field.python_type_hint, field.python_default
            ));
        }
        if fields.is_empty() {
            content.push_str("        pass\n");
        }
        content.push('\n');

        let path = self.base_py_dir.join(format!("{snake}_base.py"));
        fs::write(&path, &content)?;
        println!(
            "cargo:warning=Generated Python opaque base: {}",
            path.display()
        );
        Ok(())
    }

    /// 生成 Python 不透明类型的用户文件（仅当不存在时生成）
    pub fn write_python_user_file(&self, struct_name: &str) -> std::io::Result<()> {
        fs::create_dir_all(&self.user_py_dir)?;

        let snake = to_snake_case(struct_name);
        let path = self.user_py_dir.join(format!("{snake}.py"));

        if path.exists() {
            println!(
                "cargo:warning=Python user file exists, skipping: {}",
                path.display()
            );
            return Ok(());
        }

        let base_class = format!("{}Base", struct_name);
        let base_module = make_python_import(
            &self.user_py_dir,
            &self.base_py_dir,
            &format!("{snake}_base"),
        );

        let mut content = String::new();
        content.push_str(&format!("from {base_module} import {base_class}\n\n\n"));
        content.push_str(&format!("class {struct_name}({base_class}):\n"));
        content.push_str(&format!(
            "    \"\"\"User-defined opaque type: {struct_name}\"\"\"\n\n"
        ));
        content.push_str("    def __init__(self):\n");
        content.push_str("        super().__init__()\n");
        content.push_str("        # TODO: Add your custom initialization here\n");

        fs::write(&path, &content)?;
        println!(
            "cargo:warning=Generated Python user file: {}",
            path.display()
        );
        Ok(())
    }

    /// 为不透明类型同时生成 C++ 和 Python 的基类和用户文件
    pub fn generate_all(&self, struct_name: &str, fields: &[OpaqueField]) -> std::io::Result<()> {
        self.write_cpp_base_header(struct_name, fields)?;
        self.write_cpp_user_file(struct_name)?;
        self.write_python_base(struct_name, fields)?;
        self.write_python_user_file(struct_name)?;
        Ok(())
    }

    /// 仅生成 C++ 用户文件（不生成系统基类）
    pub fn write_cpp_user_only(
        &self,
        struct_name: &str,
        fields: &[OpaqueField],
    ) -> std::io::Result<()> {
        fs::create_dir_all(&self.user_cpp_dir)?;

        let snake = to_snake_case(struct_name);
        let path = self.user_cpp_dir.join(format!("{snake}.h"));

        self.cleanup_legacy_generated(struct_name)?;

        if path.exists() {
            println!(
                "cargo:warning=C++ opaque user file exists, skipping: {}",
                path.display()
            );
            return Ok(());
        }

        let guard = format!("ROPLAT_OPAQUE_{}_H", struct_name.to_uppercase());
        let mut content = String::new();
        content.push_str(&format!("#ifndef {guard}\n#define {guard}\n\n"));
        content.push_str(&format!("class {struct_name} {{\npublic:\n"));
        content.push_str(&format!("    {struct_name}() = default;\n"));
        content.push('\n');
        content.push_str("    // Default pattern: define the concrete fields directly here.\n");
        if fields.is_empty() {
            content.push_str("    // TODO: add your fields, constructors and methods.\n");
            content.push_str("    // Example:\n");
            content.push_str("    // double value = {};\n");
        } else {
            content.push_str("    // Suggested starter fields inferred from the Rust template:\n");
            for field in fields {
                content.push_str(&format!("    {} {} = {{}};\n", field.cpp_type, field.name));
            }
        }
        content.push('\n');
        content.push_str("    // Alternative pattern:\n");
        content
            .push_str("    // You may replace this class with an alias or wrapper around your\n");
        content.push_str(
            "    // project-specific native type if that better matches your codebase.\n",
        );
        content.push_str("};\n\n");
        content.push_str(&format!("#endif // {guard}\n"));

        fs::write(&path, &content)?;
        println!(
            "cargo:warning=Generated C++ opaque user-only file: {}",
            path.display()
        );
        Ok(())
    }

    /// 仅生成 Python 用户文件（不生成系统基类）
    pub fn write_python_user_only(
        &self,
        struct_name: &str,
        fields: &[OpaqueField],
    ) -> std::io::Result<()> {
        fs::create_dir_all(&self.user_py_dir)?;

        let snake = to_snake_case(struct_name);
        let path = self.user_py_dir.join(format!("{snake}.py"));

        self.cleanup_legacy_generated(struct_name)?;

        if path.exists() {
            println!(
                "cargo:warning=Python opaque user file exists, skipping: {}",
                path.display()
            );
            return Ok(());
        }

        let mut content = String::new();
        content.push_str(&format!("class {struct_name}:\n"));
        content.push_str("    def __init__(self):\n");
        if fields.is_empty() {
            content
                .push_str("        # Default pattern: define the concrete fields directly here.\n");
            content.push_str("        # Example: self.value = 0.0\n");
            content.push_str("        pass\n");
        } else {
            content
                .push_str("        # Suggested starter fields inferred from the Rust template.\n");
            for field in fields {
                content.push_str(&format!(
                    "        self.{}: {} = {}\n",
                    field.name, field.python_type_hint, field.python_default
                ));
            }
        }
        content.push('\n');
        content.push_str("# Alternative pattern:\n");
        content.push_str("# You may replace this class with an alias or wrapper around another\n");
        content.push_str("# project-specific Python implementation.\n");

        fs::write(&path, &content)?;
        println!(
            "cargo:warning=Generated Python opaque user-only file: {}",
            path.display()
        );
        Ok(())
    }

    fn cleanup_legacy_generated(&self, struct_name: &str) -> std::io::Result<()> {
        let snake = to_snake_case(struct_name);
        for path in [
            self.base_cpp_dir.join(format!("{snake}_base.h")),
            self.base_py_dir.join(format!("{snake}_base.py")),
        ] {
            if path.exists() {
                fs::remove_file(path)?;
            }
        }
        Ok(())
    }
}

// ═══════════════════════════════════════════════════════════════════
// NodeCodeGen — 多语言节点
// ═══════════════════════════════════════════════════════════════════

/// 节点端口方向
#[derive(Debug, Clone, PartialEq)]
pub enum PortDirection {
    /// 输入端口 (Subscriber)
    Input,
    /// 输出端口 (Publisher)
    Output,
}

/// 节点通讯端口描述
#[derive(Debug, Clone)]
pub struct NodePort {
    /// 端口名称（如 `sensor_input`）
    pub name: String,
    /// 消息类型名（如 `SensorData`）
    pub msg_type: String,
    /// 端口方向
    pub direction: PortDirection,
    pub is_opaque: bool,
}

impl NodePort {
    /// 创建一个输入端口（Subscriber）
    pub fn input(name: impl Into<String>, msg_type: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            msg_type: msg_type.into(),
            direction: PortDirection::Input,
            is_opaque: false,
        }
    }

    /// 创建一个输出端口（Publisher）
    pub fn output(name: impl Into<String>, msg_type: impl Into<String>) -> Self {
        Self {
            name: name.into(),
            msg_type: msg_type.into(),
            direction: PortDirection::Output,
            is_opaque: false,
        }
    }

    /// 标记端口承载不透明数据。
    pub fn opaque(mut self) -> Self {
        self.is_opaque = true;
        self
    }
}

/// 多语言节点描述
#[derive(Debug, Clone)]
pub struct NodeSpec {
    /// 节点名称（CamelCase，如 `CppController`）
    pub name: String,
    /// 通讯端口列表
    pub ports: Vec<NodePort>,
    /// 配置字段列表
    pub fields: Vec<OpaqueField>,
}

impl NodeSpec {
    pub fn new(name: impl Into<String>) -> Self {
        Self { name: name.into(), ports: Vec::new(), fields: Vec::new() }
    }

    /// 添加一个输入端口
    pub fn input(mut self, name: impl Into<String>, msg_type: impl Into<String>) -> Self {
        self.ports.push(NodePort::input(name, msg_type));
        self
    }

    /// 添加一个输出端口
    pub fn output(mut self, name: impl Into<String>, msg_type: impl Into<String>) -> Self {
        self.ports.push(NodePort::output(name, msg_type));
        self
    }

    /// 添加一个不透明输入端口
    pub fn opaque_input(mut self, name: impl Into<String>, msg_type: impl Into<String>) -> Self {
        self.ports.push(NodePort::input(name, msg_type).opaque());
        self
    }

    /// 添加一个不透明输出端口
    pub fn opaque_output(mut self, name: impl Into<String>, msg_type: impl Into<String>) -> Self {
        self.ports.push(NodePort::output(name, msg_type).opaque());
        self
    }

    /// 添加一个配置字段
    pub fn field(mut self, field: OpaqueField) -> Self {
        self.fields.push(field);
        self
    }

    fn inputs(&self) -> impl Iterator<Item = &NodePort> {
        self.ports
            .iter()
            .filter(|p| p.direction == PortDirection::Input)
    }

    fn outputs(&self) -> impl Iterator<Item = &NodePort> {
        self.ports
            .iter()
            .filter(|p| p.direction == PortDirection::Output)
    }

    /// 收集所有端口使用的消息类型（去重）
    fn msg_types(&self) -> Vec<&str> {
        let mut types: Vec<&str> = self
            .ports
            .iter()
            .filter(|p| !p.is_opaque)
            .map(|p| p.msg_type.as_str())
            .collect();
        types.sort();
        types.dedup();
        types
    }

    fn single_input(&self) -> std::io::Result<Option<&NodePort>> {
        let inputs: Vec<_> = self.inputs().collect();
        if inputs.len() > 1 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!(
                    "node {} currently supports at most one input port for code generation",
                    self.name
                ),
            ));
        }
        Ok(inputs.into_iter().next())
    }

    fn single_output(&self) -> std::io::Result<Option<&NodePort>> {
        let outputs: Vec<_> = self.outputs().collect();
        if outputs.len() > 1 {
            return Err(std::io::Error::new(
                std::io::ErrorKind::InvalidInput,
                format!(
                    "node {} currently supports at most one output port for code generation",
                    self.name
                ),
            ));
        }
        Ok(outputs.into_iter().next())
    }

    fn cpp_input_type(&self) -> std::io::Result<String> {
        Ok(self
            .single_input()?
            .map(|port| port.msg_type.clone())
            .unwrap_or_else(|| "void".to_string()))
    }

    fn cpp_output_type(&self) -> std::io::Result<String> {
        Ok(self
            .single_output()?
            .map(|port| port.msg_type.clone())
            .unwrap_or_else(|| "void".to_string()))
    }

    /// 输入端口是否为不透明数据
    fn input_is_opaque(&self) -> std::io::Result<bool> {
        Ok(self.single_input()?.is_some_and(|p| p.is_opaque))
    }

    /// 输出端口是否为不透明数据
    fn output_is_opaque(&self) -> std::io::Result<bool> {
        Ok(self.single_output()?.is_some_and(|p| p.is_opaque))
    }

    fn cpp_process_signature(&self) -> std::io::Result<String> {
        let input = self.single_input()?;
        let output = self.single_output()?;
        let signature = match (input, output) {
            (Some(in_port), Some(out_port)) => {
                format!(
                    "{} process(const {} &input)",
                    out_port.msg_type, in_port.msg_type
                )
            }
            (Some(in_port), None) => format!("void process(const {} &input)", in_port.msg_type),
            (None, Some(out_port)) => format!("{} process()", out_port.msg_type),
            (None, None) => "void process()".to_string(),
        };
        Ok(signature)
    }

    fn py_input_type(&self) -> std::io::Result<Option<String>> {
        Ok(self.single_input()?.map(|p| p.msg_type.clone()))
    }

    fn py_output_type(&self) -> std::io::Result<Option<String>> {
        Ok(self.single_output()?.map(|p| p.msg_type.clone()))
    }

    fn py_process_signature(&self) -> std::io::Result<String> {
        let input = self.py_input_type()?;
        let output = self.py_output_type()?;
        let sig = match (input, output) {
            (Some(i), Some(o)) => format!("def process(self, input_data: '{i}') -> '{o}':"),
            (Some(i), None) => format!("def process(self, input_data: '{i}') -> None:"),
            (None, Some(o)) => format!("def process(self) -> '{o}':"),
            (None, None) => "def process(self) -> None:".to_string(),
        };
        Ok(sig)
    }
}

/// 多语言节点代码生成器
///
/// 为多语言节点生成完整的文件结构：
///
/// - **C++ 基类** (`cpp/roplat_gen/<node>_base.h`)：仅承载 Rust 傀儡结构里显式声明的字段
/// - **C++ 用户文件** (`cpp/src/<node>.h`)：继承 `<NodeName>Base` 与 `roplat::Node<In, Out>`
/// - **Python 基类** (`py/roplat_gen/<node>_base.py`)：仅承载显式字段
/// - **Python 用户文件** (`py/<node>.py`)：继承基类并实现处理逻辑
pub struct NodeCodeGen {
    base_cpp_dir: PathBuf,
    base_py_dir: PathBuf,
    user_cpp_dir: PathBuf,
    user_py_dir: PathBuf,
}

impl NodeCodeGen {
    pub fn new(
        base_cpp_dir: impl Into<PathBuf>,
        base_py_dir: impl Into<PathBuf>,
        user_cpp_dir: impl Into<PathBuf>,
        user_py_dir: impl Into<PathBuf>,
    ) -> Self {
        Self {
            base_cpp_dir: base_cpp_dir.into(),
            base_py_dir: base_py_dir.into(),
            user_cpp_dir: user_cpp_dir.into(),
            user_py_dir: user_py_dir.into(),
        }
    }

    /// 生成 C++ 节点基类头文件（由 roplat 管理，每次构建覆盖）
    ///
    /// 内容：消息类型 include、FFI 函数声明、字段基类（`<Node>Base`）
    pub fn write_cpp_base(&self, spec: &NodeSpec) -> std::io::Result<()> {
        fs::create_dir_all(&self.base_cpp_dir)?;

        let snake = to_snake_case(&spec.name);
        let guard = format!("ROPLAT_NODE_{}_BASE_H", spec.name.to_uppercase());
        let base_class = format!("{}Base", spec.name);

        let mut h = String::new();
        h.push_str(&format!("#ifndef {guard}\n#define {guard}\n\n"));
        h.push_str("#include <cstdint>\n#include <cstddef>\n");

        // 透明消息类型头文件
        for msg in spec.msg_types() {
            h.push_str(&format!("#include \"{}.h\"\n", to_snake_case(msg)));
        }
        h.push('\n');

        // FFI 函数声明（由 _ffi.cpp 实现）
        let input_type = spec.cpp_input_type()?;
        let output_type = spec.cpp_output_type()?;
        h.push_str("extern \"C\" {\n");
        h.push_str(&format!("    void* roplat_node_{snake}_create();\n"));
        h.push_str(&format!(
            "    void  roplat_node_{snake}_destroy(void* ptr);\n"
        ));
        h.push_str(&format!("    int   roplat_node_{snake}_init(void* ptr);\n"));
        h.push_str(&format!(
            "    int   roplat_node_{snake}_shutdown(void* ptr);\n"
        ));
        let process_ffi = {
            let input_opaque = spec.input_is_opaque()?;
            let output_opaque = spec.output_is_opaque()?;
            match (input_opaque, output_opaque) {
                // 不透明输入：从 thread_local 总线读取，不通过 FFI 参数传递
                (true, true) => format!("    void  roplat_node_{snake}_process(void* ptr);\n"),
                (false, true) if input_type == "void" => {
                    format!("    void  roplat_node_{snake}_process(void* ptr);\n")
                }
                (false, true) => format!(
                    "    void  roplat_node_{snake}_process(void* ptr, const {input_type}* input);\n"
                ),
                (true, false) if output_type == "void" => {
                    format!("    void  roplat_node_{snake}_process(void* ptr);\n")
                }
                (true, false) => {
                    format!("    {output_type} roplat_node_{snake}_process(void* ptr);\n")
                }
                // 透明：原始签名
                (false, false) => match (input_type.as_str(), output_type.as_str()) {
                    ("void", "void") => {
                        format!("    void  roplat_node_{snake}_process(void* ptr);\n")
                    }
                    (_, "void") => format!(
                        "    void  roplat_node_{snake}_process(void* ptr, const {input_type}* input);\n"
                    ),
                    ("void", _) => {
                        format!("    {output_type} roplat_node_{snake}_process(void* ptr);\n")
                    }
                    _ => format!(
                        "    {output_type} roplat_node_{snake}_process(void* ptr, const {input_type}* input);\n"
                    ),
                },
            }
        };
        h.push_str(&process_ffi);
        // set_fields 声明
        if !spec.fields.is_empty() {
            let params: Vec<String> = spec
                .fields
                .iter()
                .map(|f| format!("{} {}", f.cpp_type, f.name))
                .collect();
            h.push_str(&format!(
                "    void  roplat_node_{snake}_set_fields(void* ptr, {params});\n",
                params = params.join(", ")
            ));
        }
        h.push_str("}\n\n");

        // 基类定义（仅含字段）
        h.push_str(&format!("class {base_class} {{\n"));
        h.push_str("protected:\n");

        // 配置字段
        for field in &spec.fields {
            h.push_str(&format!("    {} {} = {{}};\n", field.cpp_type, field.name));
        }
        if !spec.fields.is_empty() {
            h.push('\n');
        }

        h.push_str("\npublic:\n");
        h.push_str(&format!("    virtual ~{base_class}() = default;\n\n"));

        // 配置字段 getter/setter
        for field in &spec.fields {
            let cap = capitalize(&field.name);
            h.push_str(&format!(
                "    {} get{cap}() const {{ return {}; }}\n",
                field.cpp_type, field.name
            ));
            h.push_str(&format!(
                "    void set{cap}({} value) {{ {} = value; }}\n",
                field.cpp_type, field.name
            ));
        }
        h.push_str("};\n\n");
        h.push_str(&format!("#endif // {guard}\n"));

        let path = self.base_cpp_dir.join(format!("{snake}_base.h"));
        fs::write(&path, &h)?;
        println!("cargo:warning=Generated C++ node base: {}", path.display());
        Ok(())
    }

    /// 生成 C++ 节点用户文件（仅当不存在时生成）
    pub fn write_cpp_user(&self, spec: &NodeSpec) -> std::io::Result<()> {
        fs::create_dir_all(&self.user_cpp_dir)?;

        let snake = to_snake_case(&spec.name);
        let path = self.user_cpp_dir.join(format!("{snake}.h"));

        if path.exists() {
            println!(
                "cargo:warning=C++ node user file exists, skipping: {}",
                path.display()
            );
            return Ok(());
        }

        let guard = format!("ROPLAT_NODE_{}_H", spec.name.to_uppercase());
        let base_class = format!("{}Base", spec.name);
        let base_include = make_relative_include(
            &self.user_cpp_dir,
            &self.base_cpp_dir,
            &format!("{snake}_base.h"),
        );
        let input_type = spec.cpp_input_type()?;
        let output_type = spec.cpp_output_type()?;
        let node_template = format!("roplat::Node<{input_type}, {output_type}>");
        let process_signature = spec.cpp_process_signature()?;

        let mut h = String::new();
        h.push_str(&format!("#ifndef {guard}\n#define {guard}\n\n"));
        h.push_str(&format!("#include \"{base_include}\"\n"));
        h.push_str("#include \"roplat/node.h\"\n");
        for msg in spec.msg_types() {
            let msg_include = make_relative_include(
                &self.user_cpp_dir,
                &self.base_cpp_dir,
                &format!("{}.h", to_snake_case(msg)),
            );
            h.push_str(&format!("#include \"{msg_include}\"\n"));
        }
        h.push('\n');
        h.push_str(&format!(
            "class {} : public {base_class}, public {node_template} {{\npublic:\n",
            spec.name
        ));
        h.push_str(&format!("    {}() = default;\n", spec.name));
        h.push_str(&format!("    ~{}() override = default;\n\n", spec.name));
        h.push_str(&format!("    using Input = {input_type};\n"));
        h.push_str(&format!("    using Output = {output_type};\n\n"));

        // process 实现占位
        h.push_str(&format!("    {process_signature} override {{\n"));
        h.push_str("        // TODO: Implement your node logic here\n");
        if let Some(input) = spec.single_input()? {
            h.push_str(&format!(
                "        // Input port: {} -> {}\n",
                input.name, input.msg_type
            ));
        }
        if let Some(output) = spec.single_output()? {
            h.push_str(&format!(
                "        // Output port: {} -> {}\n",
                output.name, output.msg_type
            ));
        }
        if spec.single_output()?.is_some() {
            h.push_str("        return {};\n");
        }
        h.push_str("    }\n");

        h.push_str("};\n\n");
        h.push_str(&format!("#endif // {guard}\n"));

        fs::write(&path, &h)?;
        println!(
            "cargo:warning=Generated C++ node user file: {}",
            path.display()
        );
        Ok(())
    }

    /// 生成 Python Node 公共基类（对应 Rust `roplat::Node` trait 和 C++ `roplat::Node<In, Out>`）
    ///
    /// 写入 `roplat_gen/node.py`，多次调用不重复写入
    pub fn write_py_node_abc(&self) -> std::io::Result<()> {
        fs::create_dir_all(&self.base_py_dir)?;

        let path = self.base_py_dir.join("node.py");
        // 幂等：每次构建可覆盖（属于系统管理文件）
        let py = r#"# Auto-generated by roplat - DO NOT EDIT
from abc import ABC, abstractmethod


class Node(ABC):
    """Base class for all roplat nodes (Python side).

    Mirrors the Rust `roplat::Node` trait and C++ `roplat::Node<In, Out>` template.
    Subclasses should override `process` with a typed signature.
    """

    def on_init(self) -> None:
        """Called before the first process() invocation."""
        pass

    def on_shutdown(self) -> None:
        """Called after the last process() invocation."""
        pass

    @abstractmethod
    def process(self, *args, **kwargs):
        """Core processing logic. Subclasses must override with typed signature."""
        raise NotImplementedError
"#;

        fs::write(&path, py)?;
        println!(
            "cargo:warning=Generated Python Node base class: {}",
            path.display()
        );
        Ok(())
    }

    /// 生成 Python 节点基类文件（由 roplat 管理，每次构建覆盖）
    ///
    /// 仅包含配置字段，生命周期方法和 process 由 `Node` 公共基类提供
    pub fn write_py_base(&self, spec: &NodeSpec) -> std::io::Result<()> {
        fs::create_dir_all(&self.base_py_dir)?;

        let snake = to_snake_case(&spec.name);
        let base_class = format!("{}Base", spec.name);

        let mut py = String::new();
        py.push_str("# Auto-generated by roplat - DO NOT EDIT\n");
        py.push_str("from __future__ import annotations\n\n\n");

        py.push_str(&format!("class {base_class}:\n"));
        py.push_str(&format!(
            "    \"\"\"Field base class for node {}, managed by roplat.\"\"\"\n\n",
            spec.name
        ));

        // __init__
        py.push_str("    def __init__(self):\n");

        // 配置字段
        for field in &spec.fields {
            py.push_str(&format!(
                "        self.{}: {} = {}\n",
                field.name, field.python_type_hint, field.python_default
            ));
        }

        if spec.fields.is_empty() {
            py.push_str("        pass\n");
        }

        let path = self.base_py_dir.join(format!("{snake}_base.py"));
        fs::write(&path, &py)?;
        println!(
            "cargo:warning=Generated Python node base: {}",
            path.display()
        );
        Ok(())
    }

    /// 生成 Python 节点用户文件（仅当不存在时生成）
    pub fn write_py_user(&self, spec: &NodeSpec) -> std::io::Result<()> {
        fs::create_dir_all(&self.user_py_dir)?;

        let snake = to_snake_case(&spec.name);
        let path = self.user_py_dir.join(format!("{snake}.py"));

        if path.exists() {
            println!(
                "cargo:warning=Python node user file exists, skipping: {}",
                path.display()
            );
            return Ok(());
        }

        let base_class = format!("{}Base", spec.name);
        let base_module = make_python_import(
            &self.user_py_dir,
            &self.base_py_dir,
            &format!("{snake}_base"),
        );
        let node_module = make_python_import(&self.user_py_dir, &self.base_py_dir, "node");

        let mut py = String::new();
        py.push_str("from __future__ import annotations\n");
        py.push_str(&format!("from {base_module} import {base_class}\n"));
        py.push_str(&format!("from {node_module} import Node\n\n\n"));
        py.push_str(&format!("class {}({base_class}, Node):\n", spec.name));
        py.push_str(&format!(
            "    \"\"\"User-defined node: {}\"\"\"\n\n",
            spec.name
        ));

        py.push_str("    def __init__(self):\n");
        py.push_str("        super().__init__()\n");
        py.push_str("        # TODO: Add your custom initialization here\n\n");

        py.push_str("    ");
        py.push_str(&spec.py_process_signature()?);
        py.push('\n');
        py.push_str("        # TODO: Implement your node logic here\n");
        if let Some(input) = spec.single_input()? {
            py.push_str(&format!(
                "        # Input port: {} -> {}\n",
                input.name, input.msg_type
            ));
        }
        if let Some(output) = spec.single_output()? {
            py.push_str(&format!(
                "        # Output port: {} -> {}\n",
                output.name, output.msg_type
            ));
        }
        py.push_str("        pass\n");

        fs::write(&path, &py)?;
        println!(
            "cargo:warning=Generated Python node user file: {}",
            path.display()
        );
        Ok(())
    }

    /// 生成 C++ FFI 桥接实现文件（由 roplat 管理，每次构建覆盖）
    ///
    /// `cpp/roplat_gen/<node>_ffi.cpp` — include 用户类，实现 `extern "C"` 胶水函数
    pub fn write_cpp_ffi(&self, spec: &NodeSpec) -> std::io::Result<()> {
        fs::create_dir_all(&self.base_cpp_dir)?;

        let snake = to_snake_case(&spec.name);
        // 从 roplat_gen/ 到 src/ 的相对路径
        let user_include = make_relative_include(
            &self.base_cpp_dir,
            &self.user_cpp_dir,
            &format!("{snake}.h"),
        );
        let input_type = spec.cpp_input_type()?;
        let output_type = spec.cpp_output_type()?;
        let input_opaque = spec.input_is_opaque()?;
        let output_opaque = spec.output_is_opaque()?;

        let mut cpp = String::new();
        cpp.push_str("// Auto-generated by roplat — DO NOT EDIT\n");
        cpp.push_str(&format!("#include \"{user_include}\"\n\n"));

        // 不透明数据使用跨 TU 共享的 thread_local 总线（inline 函数保证 ODR 唯一性）
        if input_opaque || output_opaque {
            let opaque_type = if output_opaque {
                &output_type
            } else {
                &input_type
            };
            cpp.push_str(&format!(
                "inline {ot}& __roplat_opaque_bus_{ot}() {{\n\
                 \x20\x20\x20\x20static thread_local {ot} instance;\n\
                 \x20\x20\x20\x20return instance;\n}}\n\n",
                ot = opaque_type
            ));
        }

        cpp.push_str("extern \"C\" {\n\n");

        cpp.push_str(&format!(
            "void* roplat_node_{snake}_create() {{\n    return new {name}();\n}}\n\n",
            name = spec.name
        ));
        cpp.push_str(&format!(
            "void roplat_node_{snake}_destroy(void* ptr) {{\n\
             \x20\x20\x20\x20delete static_cast<{name}*>(ptr);\n}}\n\n",
            name = spec.name
        ));
        cpp.push_str(&format!(
            "int roplat_node_{snake}_init(void* ptr) {{\n\
             \x20\x20\x20\x20static_cast<{name}*>(ptr)->on_init();\n    return 0;\n}}\n\n",
            name = spec.name
        ));
        cpp.push_str(&format!(
            "int roplat_node_{snake}_shutdown(void* ptr) {{\n\
             \x20\x20\x20\x20static_cast<{name}*>(ptr)->on_shutdown();\n    return 0;\n}}\n\n",
            name = spec.name
        ));

        let process_impl = match (input_opaque, output_opaque) {
            // 输入不透明 + 输出不透明：从总线读，处理后写回总线
            (true, true) => format!(
                "void roplat_node_{snake}_process(void* ptr) {{\n\
                 \x20\x20\x20\x20__roplat_opaque_bus_{ot}() = static_cast<{name}*>(ptr)->process(__roplat_opaque_bus_{ot}());\n}}\n",
                ot = output_type,
                name = spec.name
            ),
            // 输入透明 + 输出不透明：接收透明数据，结果存入总线
            (false, true) if input_type == "void" => format!(
                "void roplat_node_{snake}_process(void* ptr) {{\n\
                 \x20\x20\x20\x20__roplat_opaque_bus_{ot}() = static_cast<{name}*>(ptr)->process();\n}}\n",
                ot = output_type,
                name = spec.name
            ),
            (false, true) => format!(
                "void roplat_node_{snake}_process(void* ptr, const {it}* input) {{\n\
                 \x20\x20\x20\x20__roplat_opaque_bus_{ot}() = static_cast<{name}*>(ptr)->process(*input);\n}}\n",
                it = input_type,
                ot = output_type,
                name = spec.name
            ),
            // 输入不透明 + 输出透明：从总线读，返回透明数据
            (true, false) if output_type == "void" => format!(
                "void roplat_node_{snake}_process(void* ptr) {{\n\
                 \x20\x20\x20\x20static_cast<{name}*>(ptr)->process(__roplat_opaque_bus_{it}());\n}}\n",
                it = input_type,
                name = spec.name
            ),
            (true, false) => format!(
                "{ot} roplat_node_{snake}_process(void* ptr) {{\n\
                 \x20\x20\x20\x20return static_cast<{name}*>(ptr)->process(__roplat_opaque_bus_{it}());\n}}\n",
                ot = output_type,
                it = input_type,
                name = spec.name
            ),
            // 双透明：原有逻辑
            (false, false) => match (input_type.as_str(), output_type.as_str()) {
                ("void", "void") => format!(
                    "void roplat_node_{snake}_process(void* ptr) {{\n\
                     \x20\x20\x20\x20static_cast<{name}*>(ptr)->process();\n}}\n",
                    name = spec.name
                ),
                (_, "void") => format!(
                    "void roplat_node_{snake}_process(void* ptr, const {it}* input) {{\n\
                     \x20\x20\x20\x20static_cast<{name}*>(ptr)->process(*input);\n}}\n",
                    it = input_type,
                    name = spec.name
                ),
                ("void", _) => format!(
                    "{ot} roplat_node_{snake}_process(void* ptr) {{\n\
                     \x20\x20\x20\x20return static_cast<{name}*>(ptr)->process();\n}}\n",
                    ot = output_type,
                    name = spec.name
                ),
                _ => format!(
                    "{ot} roplat_node_{snake}_process(void* ptr, const {it}* input) {{\n\
                     \x20\x20\x20\x20return static_cast<{name}*>(ptr)->process(*input);\n}}\n",
                    ot = output_type,
                    it = input_type,
                    name = spec.name
                ),
            },
        };
        cpp.push_str(&process_impl);

        // set_fields: 从 Rust 同步配置字段到 C++ 对象
        if !spec.fields.is_empty() {
            let params: Vec<String> = spec
                .fields
                .iter()
                .map(|f| format!("{} {}", f.cpp_type, f.name))
                .collect();
            cpp.push_str(&format!(
                "\nvoid roplat_node_{snake}_set_fields(void* ptr, {params}) {{\n\
                 \x20\x20\x20\x20auto* n = static_cast<{name}*>(ptr);\n",
                params = params.join(", "),
                name = spec.name
            ));
            for f in &spec.fields {
                let cap = capitalize(&f.name);
                cpp.push_str(&format!(
                    "\x20\x20\x20\x20n->set{cap}({name});\n",
                    name = f.name
                ));
            }
            cpp.push_str("}\n");
        }

        cpp.push_str("\n} // extern \"C\"\n");

        let path = self.base_cpp_dir.join(format!("{snake}_ffi.cpp"));
        fs::write(&path, &cpp)?;
        println!(
            "cargo:warning=Generated C++ node FFI bridge: {}",
            path.display()
        );
        Ok(())
    }

    /// 为多语言节点同时生成所有文件
    pub fn generate_cpp(&self, spec: &NodeSpec) -> std::io::Result<()> {
        self.write_cpp_base(spec)?;
        self.write_cpp_ffi(spec)?;
        self.write_cpp_user(spec)?;
        Ok(())
    }

    /// 为多语言节点同时生成 Python 文件
    pub fn generate_py(&self, spec: &NodeSpec) -> std::io::Result<()> {
        self.write_py_node_abc()?;
        self.write_py_base(spec)?;
        self.write_py_user(spec)?;
        Ok(())
    }

    /// 为多语言节点同时生成所有文件
    pub fn generate_all(&self, spec: &NodeSpec) -> std::io::Result<()> {
        self.generate_cpp(spec)?;
        self.generate_py(spec)?;
        Ok(())
    }
}

// ═══════════════════════════════════════════════════════════════════
// 共享类型和工具函数
// ═══════════════════════════════════════════════════════════════════

/// 不透明数据字段描述（同时用于 OpaqueCodeGen 和 NodeCodeGen）
#[derive(Debug, Clone)]
pub struct OpaqueField {
    pub name: String,
    pub cpp_type: String,
    pub python_type_hint: String,
    pub python_default: String,
}

impl OpaqueField {
    pub fn new(
        name: impl Into<String>,
        cpp_type: impl Into<String>,
        python_type_hint: impl Into<String>,
        python_default: impl Into<String>,
    ) -> Self {
        Self {
            name: name.into(),
            cpp_type: cpp_type.into(),
            python_type_hint: python_type_hint.into(),
            python_default: python_default.into(),
        }
    }

    pub fn f64(name: impl Into<String>) -> Self {
        Self::new(name, "double", "float", "0.0")
    }

    pub fn f32(name: impl Into<String>) -> Self {
        Self::new(name, "float", "float", "0.0")
    }

    pub fn i32(name: impl Into<String>) -> Self {
        Self::new(name, "int32_t", "int", "0")
    }

    pub fn u32(name: impl Into<String>) -> Self {
        Self::new(name, "uint32_t", "int", "0")
    }

    pub fn u64(name: impl Into<String>) -> Self {
        Self::new(name, "uint64_t", "int", "0")
    }

    pub fn bool(name: impl Into<String>) -> Self {
        Self::new(name, "bool", "bool", "False")
    }
}

/// CamelCase -> snake_case 转换
pub fn to_snake_case(s: &str) -> String {
    let mut result = String::new();
    for (i, c) in s.chars().enumerate() {
        if c.is_uppercase() {
            if i > 0 {
                result.push('_');
            }
            result.push(c.to_ascii_lowercase());
        } else {
            result.push(c);
        }
    }
    result
}

/// 首字母大写
fn capitalize(s: &str) -> String {
    let mut chars = s.chars();
    match chars.next() {
        None => String::new(),
        Some(c) => c.to_uppercase().collect::<String>() + chars.as_str(),
    }
}

/// 计算从 `from_dir` 到 `to_dir/filename` 的相对 include 路径
fn make_relative_include(from_dir: &PathBuf, to_dir: &PathBuf, filename: &str) -> String {
    if let (Ok(from), Ok(to)) = (fs::canonicalize(from_dir), fs::canonicalize(to_dir))
        && let Some(rel) = pathdiff(&to, &from)
    {
        let rel_str = rel.to_string_lossy().replace('\\', "/");
        return format!("{}/{}", rel_str, filename);
    }
    // fallback: 如果目录还不存在，尝试基于路径本身计算
    if let Some(rel) = pathdiff(to_dir, from_dir) {
        let rel_str = rel.to_string_lossy().replace('\\', "/");
        return format!("{}/{}", rel_str, filename);
    }
    filename.to_string()
}

/// 计算 Python import 路径（从 user_dir 到 base_dir 中的模块）
fn make_python_import(user_dir: &PathBuf, base_dir: &PathBuf, module_name: &str) -> String {
    // 同目录直接使用裸模块名
    if user_dir == base_dir {
        return module_name.to_string();
    }

    // 若 base_dir 是 user_dir 的子目录（例如 py/roplat_gen），则可生成包路径导入。
    if let Some(rel) = pathdiff(base_dir, user_dir) {
        let mut parts = Vec::new();
        for c in rel.components() {
            let s = c.as_os_str().to_string_lossy();
            if s == "." || s == ".." {
                return module_name.to_string();
            }
            parts.push(s.to_string());
        }
        if !parts.is_empty() {
            return format!("{}.{}", parts.join("."), module_name);
        }
    }

    // 其他场景回退为裸模块名（要求外部配置 PYTHONPATH）
    module_name.to_string()
}

/// 简单的路径差异计算（返回从 base 到 target 的相对路径）
fn pathdiff(target: &std::path::Path, base: &std::path::Path) -> Option<PathBuf> {
    let target_components: Vec<_> = target.components().collect();
    let base_components: Vec<_> = base.components().collect();

    let common_len = target_components
        .iter()
        .zip(base_components.iter())
        .take_while(|(a, b)| a == b)
        .count();

    if common_len == 0 {
        return None;
    }

    let mut result = PathBuf::new();
    for _ in common_len..base_components.len() {
        result.push("..");
    }
    for component in &target_components[common_len..] {
        result.push(component);
    }
    Some(result)
}
