use handlebars::{Handlebars, RenderError, HelperDef, RenderContext, HelperResult, Output, Context};
use serde_json::Value;
use std::fs;
use std::path::Path;

#[derive(Debug, Clone)]
pub struct TemplateEngine {
    handlebars: Handlebars<'static>,
    template_dir: String,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ApiMethod {
    pub summary: String,
    pub method_name: String,
    pub method_name_pascal: String,
    pub http_method: String,
    pub http_method_lower: String,
    pub path: String,
    pub parameters: Vec<ApiParameter>,
    pub response_type: String,
    pub is_get: bool,
    pub has_body: bool,
    pub has_query_params: bool,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct ApiParameter {
    pub name: String,
    pub type_: String,
    pub required: bool,
    pub is_body_param: bool,
    pub is_query_param: bool,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct TypeDefinition {
    pub name: String,
    pub is_enum: bool,
    pub is_interface: bool,
    pub properties: Vec<TypeProperty>,
    pub enum_values: Vec<EnumValue>,
    pub type_: String,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct TypeProperty {
    pub name: String,
    pub type_: String,
    pub required: bool,
    pub description: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct EnumValue {
    pub key: String,
    pub value: String,
    pub description: Option<String>,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct TemplateContext {
    pub folder_name: String,
    pub class_name: String,
    pub safe_folder_name: String,
    pub methods: Vec<ApiMethod>,
    pub types: Vec<TypeDefinition>,
    pub imports: Vec<String>,
    pub import_path: String,
    pub default_base_url: String,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct MainIndexContext {
    pub folders: Vec<FolderInfo>,
}

#[derive(Debug, Clone, serde::Serialize)]
pub struct FolderInfo {
    pub name: String,
    pub safe_name: String,
}

impl TemplateEngine {
    pub fn new(template_dir: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let mut handlebars = Handlebars::new();
        handlebars.set_strict_mode(false);
        // 禁用 HTML 转义，因为我们生成的是 TypeScript 代码，不是 HTML
        handlebars.register_escape_fn(handlebars::no_escape);
        
        // 注册自定义 helper
        Self::register_helpers(&mut handlebars);
        
        // 加载模板文件
        let mut template_engine = Self {
            handlebars,
            template_dir: template_dir.to_string(),
        };
        
        template_engine.load_templates()?;
        Ok(template_engine)
    }

    fn register_helpers(handlebars: &mut Handlebars) {
        // 注册 to_pascal_case helper
        handlebars.register_helper("to_pascal_case", Box::new(ToPascalCaseHelper));

        // 注册 to_camel_case helper
        handlebars.register_helper("to_camel_case", Box::new(ToCamelCaseHelper));
    }

    fn to_pascal_case(s: &str) -> String {
        let mut result = String::new();
        let mut capitalize = true;
        
        for c in s.chars() {
            if c == '_' || c == '-' {
                capitalize = true;
            } else if capitalize {
                result.push(c.to_uppercase().next().unwrap());
                capitalize = false;
            } else {
                result.push(c);
            }
        }
        
        result
    }

    fn to_camel_case(s: &str) -> String {
        let mut result = String::new();
        let mut capitalize = false;
        
        for c in s.chars() {
            if c == '_' || c == '-' {
                capitalize = true;
            } else if capitalize {
                result.push(c.to_uppercase().next().unwrap());
                capitalize = false;
            } else {
                result.push(c);
            }
        }
        
        result
    }

    fn load_templates(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let template_dir = self.template_dir.clone();
        let template_path = Path::new(&template_dir);
        
        // 首先尝试从文件系统加载模板
        if template_path.exists() {
            self.load_templates_from_filesystem(template_path)
        } else {
            // 如果文件系统模板不存在，尝试加载内置模板
            self.load_builtin_templates()
        }
    }
    
    fn load_templates_from_filesystem(&mut self, template_path: &Path) -> Result<(), Box<dyn std::error::Error>> {
        // 加载所有 .hbs 文件
        for entry in fs::read_dir(template_path)? {
            let entry = entry?;
            let path = entry.path();
            
            if path.extension().and_then(|s| s.to_str()) == Some("hbs") {
                let template_name = path.file_stem()
                    .and_then(|s| s.to_str())
                    .ok_or("无效的模板文件名")?;
                
                let template_content = fs::read_to_string(&path)?;
                self.handlebars.register_template_string(template_name, template_content)?;
            }
        }

        Ok(())
    }
    
    fn load_builtin_templates(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        // 内置模板内容
        let builtin_templates = include_str!("../templates/oboss_api.hbs");
        self.handlebars.register_template_string("oboss_api", builtin_templates.to_string())?;
        
        let builtin_templates = include_str!("../templates/types.hbs");
        self.handlebars.register_template_string("types", builtin_templates.to_string())?;
        
        let builtin_templates = include_str!("../templates/index.hbs");
        self.handlebars.register_template_string("index", builtin_templates.to_string())?;
        
        let builtin_templates = include_str!("../templates/main_index.hbs");
        self.handlebars.register_template_string("main_index", builtin_templates.to_string())?;
        
        let builtin_templates = include_str!("../templates/api_client.hbs");
        self.handlebars.register_template_string("api_client", builtin_templates.to_string())?;
        
        let builtin_templates = include_str!("../templates/advanced_api_client.hbs");
        self.handlebars.register_template_string("advanced_api_client", builtin_templates.to_string())?;
        
        let builtin_templates = include_str!("../templates/react_hooks.hbs");
        self.handlebars.register_template_string("react_hooks", builtin_templates.to_string())?;

        Ok(())
    }

    pub fn render_api_client(&self, context: &TemplateContext) -> Result<String, RenderError> {
        self.handlebars.render("api_client", context)
    }

    pub fn render_advanced_api_client(&self, context: &TemplateContext) -> Result<String, RenderError> {
        self.handlebars.render("advanced_api_client", context)
    }

    pub fn render_react_hooks(&self, context: &TemplateContext) -> Result<String, RenderError> {
        self.handlebars.render("react_hooks", context)
    }

    pub fn render_oboss_api(&self, context: &TemplateContext) -> Result<String, RenderError> {
        self.handlebars.render("oboss_api", context)
    }

    pub fn render_types(&self, context: &TemplateContext) -> Result<String, RenderError> {
        self.handlebars.render("types", context)
    }

    pub fn render_index(&self, context: &TemplateContext) -> Result<String, RenderError> {
        self.handlebars.render("index", context)
    }

    pub fn render_main_index(&self, context: &MainIndexContext) -> Result<String, RenderError> {
        self.handlebars.render("main_index", context)
    }

    pub fn render_custom_template(&self, template_name: &str, context: &Value) -> Result<String, RenderError> {
        self.handlebars.render(template_name, context)
    }

    pub fn list_available_templates(&self) -> Vec<String> {
        self.handlebars.get_templates()
            .keys()
            .cloned()
            .collect()
    }
}

// Helper 结构体
struct ToPascalCaseHelper;

impl HelperDef for ToPascalCaseHelper {
    fn call<'reg: 'rc, 'rc>(
        &self,
        h: &handlebars::Helper<'reg, 'rc>,
        _: &'reg Handlebars<'reg>,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
        out: &mut dyn Output,
    ) -> HelperResult {
        if let Some(value) = h.param(0) {
            let s = value.value().as_str().unwrap_or("");
            let pascal = TemplateEngine::to_pascal_case(s);
            out.write(&pascal)?;
        }
        Ok(())
    }
}

struct ToCamelCaseHelper;

impl HelperDef for ToCamelCaseHelper {
    fn call<'reg: 'rc, 'rc>(
        &self,
        h: &handlebars::Helper<'reg, 'rc>,
        _: &'reg Handlebars<'reg>,
        _: &'rc Context,
        _: &mut RenderContext<'reg, 'rc>,
        out: &mut dyn Output,
    ) -> HelperResult {
        if let Some(value) = h.param(0) {
            let s = value.value().as_str().unwrap_or("");
            let camel = TemplateEngine::to_camel_case(s);
            out.write(&camel)?;
        }
        Ok(())
    }
}
