//! 接入包模板分发（基线档）。
//!
//! 版本化分发（T1，2026-07-03）：AGENTS.md / git-workflow.md 的炼境管理内容
//! 包在 ONBOARDING_MANAGED 标记块中——文件不存在写全新；已有标记原位升级块；
//! 无标记则末尾追加块。块外用户内容永不触碰。
//! lefthook.yml 因用户常深度定制（monorepo filter 等），保持"存在即跳过"。
//! 模板蓝本取自 enterprise-system，已通用化。

use serde::{Deserialize, Serialize};
use std::path::Path;

const MANAGED_START: &str = "<!-- ONBOARDING_MANAGED_START";
const MANAGED_END: &str = "<!-- ONBOARDING_MANAGED_END -->";

// 标准模板（编译期内嵌）
const TMPL_AGENTS: &str = include_str!("../../resources/onboarding/AGENTS.md.tmpl");
const TMPL_LEFTHOOK: &str = include_str!("../../resources/onboarding/lefthook.yml.tmpl");
const TMPL_GIT_WORKFLOW: &str = include_str!("../../resources/onboarding/git-workflow.md.tmpl");

/// 单个模板的分发结果。
#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)]
pub struct TemplateResult {
    /// 相对项目根的目标路径
    pub name: String,
    /// "written" | "skipped" | "failed"
    pub action: String,
    pub detail: String,
}

/// 分发单个模板：存在则跳过（绝不覆盖），不存在则写入。
fn dispatch_one(root: &Path, relative: &str, content: &str) -> TemplateResult {
    let target = root.join(relative);
    if target.exists() {
        return TemplateResult {
            name: relative.to_string(),
            action: "skipped".into(),
            detail: "已存在，未覆盖".into(),
        };
    }
    if let Some(parent) = target.parent() {
        if let Err(e) = std::fs::create_dir_all(parent) {
            return TemplateResult {
                name: relative.to_string(),
                action: "failed".into(),
                detail: format!("建目录失败: {e}"),
            };
        }
    }
    match std::fs::write(&target, content) {
        Ok(_) => TemplateResult {
            name: relative.to_string(),
            action: "written".into(),
            detail: "已写入".into(),
        },
        Err(e) => TemplateResult {
            name: relative.to_string(),
            action: "failed".into(),
            detail: format!("写入失败: {e}"),
        },
    }
}

/// 从模板中提取 ONBOARDING_MANAGED 块（含首尾标记行）。
fn extract_managed_block(template: &str) -> Option<&str> {
    let start = template.find(MANAGED_START)?;
    let end = template.find(MANAGED_END)? + MANAGED_END.len();
    template.get(start..end)
}

/// 从标记行提取版本号（`version="x.y.z"`）。
fn block_version(text: &str) -> Option<&str> {
    let start = text.find(MANAGED_START)?;
    let rest = &text[start..];
    let v = rest.find("version=\"")? + "version=\"".len();
    let tail = &rest[v..];
    tail.split('"').next()
}

/// 版本化分发（三态）：不存在→写全新；有标记→原位升级块；无标记→末尾追加块。
fn dispatch_versioned(root: &Path, relative: &str, template: &str) -> TemplateResult {
    let target = root.join(relative);
    let tpl_block = match extract_managed_block(template) {
        Some(b) => b,
        None => return dispatch_one(root, relative, template), // 模板无标记，退化为旧行为
    };
    let tpl_ver = block_version(tpl_block).unwrap_or("?");

    if !target.exists() {
        return dispatch_one(root, relative, template);
    }
    let content = match std::fs::read_to_string(&target) {
        Ok(c) => c,
        Err(e) => {
            return TemplateResult {
                name: relative.to_string(),
                action: "failed".into(),
                detail: format!("读取失败: {e}"),
            }
        }
    };

    let (new_content, action, detail) = if let (Some(s), Some(e)) =
        (content.find(MANAGED_START), content.find(MANAGED_END))
    {
        let old_ver = block_version(&content).unwrap_or("?").to_string();
        if old_ver == tpl_ver {
            return TemplateResult {
                name: relative.to_string(),
                action: "skipped".into(),
                detail: format!("管理块已是 v{tpl_ver}"),
            };
        }
        let mut nc = String::new();
        nc.push_str(&content[..s]);
        nc.push_str(tpl_block);
        nc.push_str(&content[e + MANAGED_END.len()..]);
        (nc, "updated", format!("管理块 v{old_ver} → v{tpl_ver}，块外内容未动"))
    } else {
        (
            format!("{}\n\n{}\n", content.trim_end(), tpl_block),
            "appended",
            format!("追加管理块 v{tpl_ver}（存量文件无标记，原内容保留在上方）"),
        )
    };

    match std::fs::write(&target, new_content) {
        Ok(_) => TemplateResult {
            name: relative.to_string(),
            action: action.into(),
            detail,
        },
        Err(e) => TemplateResult {
            name: relative.to_string(),
            action: "failed".into(),
            detail: format!("写入失败: {e}"),
        },
    }
}

/// 向项目分发全部基线模板，返回每个的处理结果。
/// project_path 为项目根（WSL 项目为 \\wsl.localhost\... UNC 路径，可直接写）。
pub fn dispatch_templates(project_path: &str) -> Vec<TemplateResult> {
    let root = Path::new(project_path.trim_end_matches(['/', '\\']));
    vec![
        dispatch_versioned(root, "AGENTS.md", TMPL_AGENTS),
        dispatch_one(root, "lefthook.yml", TMPL_LEFTHOOK),
        dispatch_versioned(root, "docs/ai-context/git-workflow.md", TMPL_GIT_WORKFLOW),
    ]
}

/// 接入包应用报告：模板分发结果 + buff 状态。
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct OnboardingReport {
    pub templates: Vec<TemplateResult>,
    pub buff: crate::commands::buff::BuffStatus,
}

/// 应用接入包（基线档）：分发标准模板 + 挂 buff（炼境登记 + git 观测/飞轮采集）。
#[tauri::command]
pub fn apply_onboarding_pack(
    project_path: String,
    project_id: String,
) -> Result<OnboardingReport, String> {
    let templates = dispatch_templates(&project_path);
    let buff = crate::commands::buff::apply_blueprint_buff(project_path, project_id)?;
    Ok(OnboardingReport { templates, buff })
}

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

    #[test]
    fn writes_then_skips_never_overwrites() {
        let base = std::env::temp_dir()
            .join(format!("lianjing_onboarding_test_{}", std::process::id()));
        let _ = std::fs::remove_dir_all(&base);
        std::fs::create_dir_all(&base).unwrap();
        let pp = base.to_str().unwrap();

        // 首次：全部写入
        let r1 = dispatch_templates(pp);
        assert!(r1.iter().all(|t| t.action == "written"), "首次应全部写入");
        assert!(base.join("AGENTS.md").exists());
        assert!(base.join("lefthook.yml").exists());
        assert!(base.join("docs/ai-context/git-workflow.md").exists());

        // 改一个文件（无管理块标记），验证用户内容不被覆盖：
        // T1 后的契约——AGENTS/git-workflow 追加管理块（原内容保留），lefthook 仍跳过
        std::fs::write(base.join("AGENTS.md"), "用户自己的内容").unwrap();
        let r2 = dispatch_templates(pp);
        let agents_r = r2.iter().find(|t| t.name == "AGENTS.md").unwrap();
        assert_eq!(agents_r.action, "appended", "无标记存量文件应追加管理块");
        let kept = std::fs::read_to_string(base.join("AGENTS.md")).unwrap();
        assert!(kept.starts_with("用户自己的内容"), "用户内容必须保留在原位");
        let lefthook_r = r2.iter().find(|t| t.name == "lefthook.yml").unwrap();
        assert_eq!(lefthook_r.action, "skipped", "lefthook 保持存在即跳过");

        let _ = std::fs::remove_dir_all(&base);
    }

    // ── T1：版本化三态分发 ────────────────────────────────────────────────

    #[test]
    fn versioned_dispatch_three_states() {
        let base = std::env::temp_dir()
            .join(format!("lianjing_onb_ver_test_{}", std::process::id()));
        let _ = std::fs::remove_dir_all(&base);
        std::fs::create_dir_all(&base).unwrap();
        let pp = base.to_str().unwrap();

        // 态一：不存在 → 写全新（含管理块标记）
        let r1 = dispatch_templates(pp);
        assert!(r1.iter().all(|t| t.action == "written"));
        let agents = std::fs::read_to_string(base.join("AGENTS.md")).unwrap();
        assert!(agents.contains(MANAGED_START) && agents.contains(MANAGED_END));

        // 态二（同版本）：有标记且版本一致 → skipped，文件不动
        let r2 = dispatch_templates(pp);
        let agents_r = r2.iter().find(|t| t.name == "AGENTS.md").unwrap();
        assert_eq!(agents_r.action, "skipped");

        // 态二（旧版本）：把版本改旧 + 块外加用户内容 → updated，块升级、用户内容保留
        let old = agents
            .replace("version=\"1.1.0\"", "version=\"1.0.0\"")
            .replace("## 项目概况", "## 项目概况

我的项目说明")
            + "
## 用户自加的章节
内容
";
        std::fs::write(base.join("AGENTS.md"), &old).unwrap();
        let r3 = dispatch_templates(pp);
        let agents_r = r3.iter().find(|t| t.name == "AGENTS.md").unwrap();
        assert_eq!(agents_r.action, "updated");
        let upgraded = std::fs::read_to_string(base.join("AGENTS.md")).unwrap();
        assert!(upgraded.contains("version=\"1.1.0\""), "块应升级到模板版本");
        assert!(upgraded.contains("我的项目说明"), "块外用户内容（前）须保留");
        assert!(upgraded.contains("用户自加的章节"), "块外用户内容（后）须保留");
        assert_eq!(upgraded.matches(MANAGED_START).count(), 1, "只有一个管理块");

        // 态三：存量文件无标记 → appended，原内容保留在上方
        std::fs::write(base.join("AGENTS.md"), "# 手写的 AGENTS

自定义内容").unwrap();
        let r4 = dispatch_templates(pp);
        let agents_r = r4.iter().find(|t| t.name == "AGENTS.md").unwrap();
        assert_eq!(agents_r.action, "appended");
        let appended = std::fs::read_to_string(base.join("AGENTS.md")).unwrap();
        assert!(appended.starts_with("# 手写的 AGENTS"));
        assert!(appended.contains(MANAGED_START));

        // lefthook 始终保持"存在即跳过"
        let lefthook_r = r4.iter().find(|t| t.name == "lefthook.yml").unwrap();
        assert_eq!(lefthook_r.action, "skipped");

        let _ = std::fs::remove_dir_all(&base);
    }
}

