/// Initialize a project implementation from a template source.
///
/// # Errors
///
/// Returns `Err(KamError)` if filesystem operations, archive extraction, or template rendering fail.
pub fn init_impl(path: &Path, params: &mut InitImplParams<'_>) -> Result<(), KamError> {
    let temp_dir = tempfile::tempdir().map_err(KamError::Io)?;
    let template_path = resolve_template_source(params.impl_source, &temp_dir)?;
    let archive_id = params
        .explicit_template_id
        .map_or_else(|| "template".to_string(), ToString::to_string);

    let template_kam_path = template_path.join("kam.toml");
    if template_kam_path.exists() {
        merge_template_defaults(&template_kam_path, params.template_vars)?;
    }

    let mut kt = new_project_kam_toml(params, &archive_id);
    let kam_vars = take_kam_vars(params.template_vars);
    if !kam_vars.is_empty() {
        kt.apply_vars(kam_vars)?;
    }

    if !path.exists() {
        fs::create_dir_all(path).map_err(KamError::Io)?;
    }

    merge_kam_toml_vars(&kt, params.template_vars);

    // Check if kam.toml exists BEFORE copy_and_replace
    let kam_toml_path = path.join("kam.toml");
    let kam_toml_existed_before_copy = kam_toml_path.exists();

    // Copy files (respect build.include/build.exclude if present)
    let excludes = kt.kam.build.as_ref().and_then(|b| b.exclude.as_deref());
    let includes = kt.kam.build.as_ref().and_then(|b| b.include.as_deref());

    crate::template::TemplateManager::copy_and_replace_with_rules(
        &template_path,
        path,
        params.template_vars,
        params.force,
        &archive_id,
        excludes,
        includes,
    )?;

    // If kam.toml doesn't exist (not in template), write the generated one
    if !kam_toml_path.exists() {
        kt.write_to_dir(path)?;
    }

    render_project_kam_toml(
        &kam_toml_path,
        params,
        &mut kt,
        kam_toml_existed_before_copy,
    )?;
    write_template_env_file(path, &kt, params.template_vars)?;

    Ok(())
}

/* legacy wrapper removed - use `InitTemplateParams`-based signature:
`init_template(path, InitTemplateParams { ... })` */

/// Create a project from a named template.
///
/// # Errors
///
/// Returns `Err(KamError)` when the template cannot be found or I/O/rendering fails.
// TODO: split this function into smaller units to reduce complexity
/// AUTO-GENERATED BY Kam/scripts/add_missing_docs.py: placeholder documentation for public function 'init_template'. Replace with real documentation. (2025-12-30T22:23:21Z)
#[allow(clippy::too_many_lines)]
pub fn init_template(path: &Path, params: &InitTemplateParams<'_>) -> Result<(), KamError> {
    let mut template_vars = crate::template::TemplateManager::parse_template_vars(params.var)?;

    let template_spec = params
        .impl_template
        .as_deref()
        .unwrap_or("kam_template")
        .to_string();

    // Strategy:
    // 1. Check if `template_spec` exists as a local file or directory (relative to CWD).
    // 2. Check if `template_spec` exists in built-in assets.
    // 3. Check project-local `tmpl/` or `templates/` dirs.
    // 4. Check global template cache.
    // 5. If NOT found and `template_spec` does NOT end with `_template` or look like a file archive,
    //    append `_template` and retry steps 2-3 (but not step 1 again, usually).

    let mut potential_names = vec![template_spec.clone()];

    // If it doesn't have an extension and doesn't end in _template, try appending it as a fallback
    let is_archive_or_path = template_spec.contains('/')
        || template_spec.contains('\\')
        || template_spec.to_ascii_lowercase().ends_with(".tar.gz")
        || std::path::Path::new(&template_spec)
            .extension()
            .is_some_and(|ext| ext.eq_ignore_ascii_case("zip"));

    if !template_spec.ends_with("_template") && !is_archive_or_path {
        potential_names.push(format!("{template_spec}_template"));
    }

    for spec in &potential_names {
        // 1. Direct local path (only for the first/raw spec, or if we really want to support 'foo_template' local dir)
        let spec_path = Path::new(spec);
        if spec_path.exists() {
            return init_from_source(path, params, &mut template_vars, spec_path, spec.as_str());
        }

        // 2. Built-in assets
        // We typically store built-ins as "kam_template.tar.gz" -> check spec
        // If the spec is "kam", we might be in the second iteration where spec="kam_template"
        let asset_name = format!("{spec}.tar.gz");
        if let Some(asset) = crate::assets::tmpl::TmplAssets::get(&asset_name) {
            let temp_dir = tempfile::tempdir().map_err(KamError::Io)?;
            let temp_path = temp_dir.path().join(format!("{spec}.tar.gz"));
            fs::write(&temp_path, &asset.data).map_err(KamError::Io)?;

            return init_from_source(path, params, &mut template_vars, &temp_path, spec.as_str());
        }

        // 3. Project-local folder search (tmpl/ or templates/)
        for candidate in project_local_template_candidates(spec) {
            if candidate.exists() {
                return init_from_source(
                    path,
                    params,
                    &mut template_vars,
                    &candidate,
                    spec.as_str(),
                );
            }
        }

        // 4. Cache
        if let Ok(Some(cached_path)) =
            crate::template::TemplateCacheManager::resolve_template_path(spec)
        {
            return init_from_source(
                path,
                params,
                &mut template_vars,
                &cached_path,
                spec.as_str(),
            );
        }
    }

    // Final failure
    Err(KamError::TemplateNotFound(format!(
        "Template '{template_spec}' not found in built-in assets, local path, cache, or project directories."
    )))
}

#[cfg(test)]
mod tests {
    use super::merge_template_defaults;
    use std::collections::HashMap;

    #[test]
    fn merge_template_defaults_reads_kam_tmpl_variables() {
        let temp_dir = tempfile::tempdir().expect("tempdir");
        let kam_toml_path = temp_dir.path().join("kam.toml");
        std::fs::write(
            &kam_toml_path,
            r#"
[prop]
id = "example_template"
name = "Example Template"
version = "v1.0.0"
versionCode = 1
description = "Example"

[kam]
module_type = "template"

[kam.tmpl.variables.repository]
var_type = "string"
required = false
default = "https://github.com/example/repo"
"#,
        )
        .expect("write kam.toml");

        let mut vars = HashMap::new();
        merge_template_defaults(&kam_toml_path, &mut vars).expect("merge defaults");

        assert_eq!(
            vars.get("repository").map(String::as_str),
            Some("https://github.com/example/repo")
        );
    }

    #[test]
    fn merge_template_defaults_does_not_override_user_vars() {
        let temp_dir = tempfile::tempdir().expect("tempdir");
        let kam_toml_path = temp_dir.path().join("kam.toml");
        std::fs::write(
            &kam_toml_path,
            r#"
[prop]
id = "example_template"
name = "Example Template"
version = "v1.0.0"
versionCode = 1
description = "Example"

[kam]
module_type = "template"

[kam.tmpl.variables.repository]
var_type = "string"
required = false
default = "https://github.com/example/repo"
"#,
        )
        .expect("write kam.toml");

        let mut vars = HashMap::from([(
            "repository".to_string(),
            "https://github.com/user/repo".to_string(),
        )]);
        merge_template_defaults(&kam_toml_path, &mut vars).expect("merge defaults");

        assert_eq!(
            vars.get("repository").map(String::as_str),
            Some("https://github.com/user/repo")
        );
    }
}
