use crate::errors::KamError;
use crate::types::kam_toml::KamToml;

use serde_json::Value;
use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::{Path, PathBuf};
use tera::{Context, Tera};
use walkdir::WalkDir;

// Import assets
use crate::assets::tmpl::TmplAssets;
use crate::utils::{PrintOp, Utils};

/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public struct 'TemplateCacheManager'. Replace with real documentation. (2025-12-30T22:23:21Z)

pub struct TemplateCacheManager;

impl TemplateCacheManager {
    // 获取模板缓存目录
    // 优先级：环境变量 > 全局配置 > 默认值
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public function 'get_cache_dir'. Replace with real documentation. (2025-12-30T22:23:21Z)
    ///
    /// # Errors
    /// Returns `KamError` if directory creation fails or kam home directory cannot be determined.
    pub fn get_cache_dir() -> Result<PathBuf, KamError> {
        // 1) 环境变量覆盖（最高优先级）
        //    允许临时或测试时覆盖，比如 KAM_TEMPLATE_CACHE_DIR=/tmp/kam_templates
        if let Ok(dir_str) = std::env::var("KAM_TEMPLATE_CACHE_DIR")
            && !dir_str.trim().is_empty()
        {
            let cache_dir = if dir_str.starts_with("~/") {
                let rel = dir_str.trim_start_matches("~/").to_string();
                dirs::home_dir().map_or_else(|| PathBuf::from(&dir_str), |home| home.join(&rel))
            } else {
                PathBuf::from(dir_str)
            };

            if !cache_dir.exists() {
                fs::create_dir_all(&cache_dir).map_err(KamError::Io)?;
            }
            return Ok(cache_dir);
        }

        // 2) 全局配置覆盖（KAM_HOME/config.toml 或默认 ~/.kam/config.toml）
        //    如果配置里有：
        //      [tmpl]
        //      cache_dir = "/some/path" (或 "~/somepath")
        //    就用那个作为模板缓存目录
        if let Ok(cfg_home) = crate::utils::kam_home_dir() {
            let cfg_path = cfg_home.join("config.toml");
            if cfg_path.exists()
                && let Ok(cfg_str) = fs::read_to_string(&cfg_path)
                && let Ok(cfg_v) = toml::from_str::<toml::Value>(&cfg_str)
                && let Some(tmpl_table) = cfg_v.get("tmpl")
                && let Some(cache_val) = tmpl_table.get("cache_dir").and_then(|v| v.as_str())
            {
                let cache_dir = if cache_val.starts_with("~/") {
                    // Keep tilde-expansion semantics relative to the user's HOME
                    dirs::home_dir().map_or_else(
                        || PathBuf::from(cache_val),
                        |home| home.join(cache_val.trim_start_matches("~/")),
                    )
                } else {
                    PathBuf::from(cache_val)
                };
                if !cache_dir.exists() {
                    fs::create_dir_all(&cache_dir).map_err(KamError::Io)?;
                }
                return Ok(cache_dir);
            }
        }

        // 3) 默认回退到 Kam home 的 templates 目录（KAM_HOME 默认为 $HOME/.kam）
        //    如果前面都没找到，就用这个
        let base = crate::utils::kam_home_dir()?;
        let cache_dir = base.join("templates");
        if !cache_dir.exists() {
            fs::create_dir_all(&cache_dir).map_err(KamError::Io)?;
        }
        Ok(cache_dir)
    }

    // 列出所有可用的模板（内置的 + 缓存的）
    // 用HashSet去重，避免重复
    /// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public function 'list_templates'. Replace with real documentation. (2025-12-30T22:23:21Z)
    #[must_use]
    pub fn list_templates() -> Vec<String> {
        let mut templates = HashSet::new();

        // 1. 从assets里找内置模板
        for file in TmplAssets::iter() {
            let filename = file.as_ref();
            if filename.ends_with(".tar.gz") {
                if let Some(name) = filename.strip_suffix(".tar.gz") {
                    templates.insert(name.to_string());
                }
            } else if has_ext(filename, "zip")
                && let Some(name) = filename.strip_suffix(".zip")
            {
                templates.insert(name.to_string());
            }
        }

        // 2. 本地缓存的模板
        //    遍历缓存目录，找出所有模板文件
        if let Ok(cache_dir) = Self::get_cache_dir()
            && let Ok(entries) = fs::read_dir(cache_dir)
        {
            for entry in entries.flatten() {
                let path = entry.path();
                if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
                    // 处理.tar.gz的特殊情况，file_stem会返回"template.tar"
                    // 需要再去掉.tar后缀
                    let name = if path.to_string_lossy().ends_with(".tar.gz") {
                        stem.strip_suffix(".tar").unwrap_or(stem)
                    } else {
                        stem
                    };
                    templates.insert(name.to_string());
                }
            }
        }

        // 3. Project-local templates (e.g., tmpl/ and templates/ directories in the project)
        //    Support both directory-based templates and archive files such as .tar.gz, .tgz, .zip, .tar
        let project_local_dirs = crate::utils::PROJECT_TEMPLATE_DIRS;
        for dir in project_local_dirs {
            let dir_path = Path::new(dir);
            if dir_path.exists()
                && dir_path.is_dir()
                && let Ok(entries) = fs::read_dir(dir_path)
            {
                for entry in entries.flatten() {
                    let p = entry.path();
                    if p.is_dir() {
                        if let Some(name) = p.file_name().and_then(|s| s.to_str()) {
                            templates.insert(name.to_string());
                        }
                    } else if let Some(filename) = p.file_name().and_then(|s| s.to_str()) {
                        // Handle common archive extensions
                        if has_tar_gz_suffix(filename) {
                            if let Some(name) = filename.strip_suffix(".tar.gz") {
                                templates.insert(name.to_string());
                            }
                        } else if has_ext(filename, "tgz") {
                            if let Some(name) = filename.strip_suffix(".tgz") {
                                templates.insert(name.to_string());
                            }
                        } else if has_ext(filename, "zip") {
                            if let Some(name) = filename.strip_suffix(".zip") {
                                templates.insert(name.to_string());
                            }
                        } else if has_ext(filename, "tar")
                            && let Some(name) = filename.strip_suffix(".tar")
                        {
                            templates.insert(name.to_string());
                        }
                    }
                }
            }
        }

        let mut list: Vec<String> = templates.into_iter().collect();
        list.sort();
        list
    }

    /// Check if a template exists (built-in or cached)
    /// Ensure a template exists (either built-in or already installed in local cache).
    ///
    /// # Errors
    /// Returns `KamError` if template resolution/installation fails.
    pub fn ensure_template(template: &str) -> Result<(), KamError> {
        let list = Self::list_templates();
        if list.contains(&template.to_string()) {
            Ok(())
        } else {
            Err(KamError::TemplateNotFound(format!(
                "Template '{template}' not found in built-in assets or local cache"
            )))
        }
    }

    /// Get path to template archive/directory.
    ///
    /// # Errors
    /// Returns `KamError` if the cache directory cannot be accessed.
    pub fn resolve_template_path(template: &str) -> Result<Option<PathBuf>, KamError> {
        if let Some(local_path) = Self::resolve_project_template_path(template) {
            return Ok(Some(local_path));
        }

        let cache_dir = Self::get_cache_dir()?;

        // Check for directory
        let dir_path = cache_dir.join(template);
        if dir_path.exists() && dir_path.is_dir() {
            return Ok(Some(dir_path));
        }

        // Check for archives
        let extensions = [".tar.gz", ".tgz", ".zip"];
        for ext in extensions {
            let archive_path = cache_dir.join(format!("{template}{ext}"));
            if archive_path.exists() {
                return Ok(Some(archive_path));
            }
        }

        Ok(None)
    }

    fn resolve_project_template_path(template: &str) -> Option<PathBuf> {
        for dir in crate::utils::PROJECT_TEMPLATE_DIRS {
            let base = Path::new(dir);
            if !base.is_dir() {
                continue;
            }

            let dir_path = base.join(template);
            if dir_path.is_dir() {
                return Some(dir_path);
            }

            for ext in [".tar.gz", ".tgz", ".zip"] {
                let archive_path = base.join(format!("{template}{ext}"));
                if archive_path.is_file() {
                    return Some(archive_path);
                }
            }
        }

        None
    }

    /// List local cached templates
    ///
    /// # Errors
    /// Returns `KamError` if the cache directory cannot be accessed or read.
    pub fn list_local_templates() -> Result<Vec<String>, KamError> {
        let cache_dir = Self::get_cache_dir()?;
        let mut templates = Vec::new();
        if cache_dir.exists() {
            for entry in fs::read_dir(cache_dir).map_err(KamError::Io)? {
                let entry = entry.map_err(KamError::Io)?;
                let path = entry.path();
                if let Some(stem) = path.file_stem().and_then(|s| s.to_str()) {
                    let name = if path.to_string_lossy().ends_with(".tar.gz") {
                        stem.strip_suffix(".tar").unwrap_or(stem)
                    } else {
                        stem
                    };
                    templates.push(name.to_string());
                }
            }
        }
        templates.sort();
        Ok(templates)
    }

    /// Install a template from a local path (directory or archive)
    ///
    /// # Errors
    /// Returns `KamError` if the source does not exist, template already exists, or copy/installation fails.
    pub fn install_template(name: &str, source: &Path) -> Result<(), KamError> {
        let cache_dir = Self::get_cache_dir()?;

        if source.is_dir() {
            let dest = cache_dir.join(name);
            if dest.exists() {
                return Err(KamError::CommandFailed(format!(
                    "Template '{name}' already exists in cache"
                )));
            }
            crate::utils::copy_dir_all(source, &dest).map_err(KamError::Io)?;
        } else if source.is_file() {
            let filename = source
                .file_name()
                .ok_or_else(|| KamError::InvalidDirectory("Invalid source filename".to_string()))?
                .to_string_lossy();

            let dest_name = if filename.ends_with(".tar.gz") {
                format!("{name}.tar.gz")
            } else if let Some(ext) = source.extension().and_then(|s| s.to_str()) {
                format!("{name}.{ext}")
            } else {
                name.to_string()
            };

            let dest = cache_dir.join(dest_name);
            if dest.exists() {
                return Err(KamError::CommandFailed(format!(
                    "Template '{name}' already exists in cache"
                )));
            }
            fs::copy(source, &dest).map_err(KamError::Io)?;
        } else {
            return Err(KamError::InvalidDirectory(format!(
                "Source '{}' does not exist",
                source.display()
            )));
        }
        Ok(())
    }

    /// Remove a template from cache
    ///
    /// # Errors
    /// Returns `KamError` if the template is not found in cache or removal fails.
    pub fn remove_template(name: &str) -> Result<(), KamError> {
        let cache_dir = Self::get_cache_dir()?;

        // Check for directory
        let dir_path = cache_dir.join(name);
        if dir_path.exists() && dir_path.is_dir() {
            fs::remove_dir_all(&dir_path).map_err(KamError::Io)?;
            return Ok(());
        }

        // Check for archives
        let extensions = [".tar.gz", ".tgz", ".zip"];
        for ext in extensions {
            let archive_path = cache_dir.join(format!("{name}{ext}"));
            if archive_path.exists() {
                fs::remove_file(&archive_path).map_err(KamError::Io)?;
                return Ok(());
            }
        }

        Err(KamError::TemplateNotFound(format!(
            "Template '{name}' not found in cache"
        )))
    }
}

/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public struct 'TemplateVariableProcessor'. Replace with real documentation. (2025-12-30T22:23:21Z)

pub struct TemplateVariableProcessor;

