use crate::db::pool;
use crate::mcp_inject;
use crate::models::{ActivityLog, Project};
use rusqlite::params;
use serde::{Deserialize, Serialize};
use serde_json::Value;

/// JOIN 视图 SQL：project_workspaces + project_profiles + git_repos(可选)
/// git_repos 数据优先于 profile（COALESCE），部署信息来自 git_repos
const PROJECT_VIEW_SQL: &str = "
    SELECT
        pw.id,
        pw.id                                               AS workspace_id,
        pp.id                                               AS profile_id,
        COALESCE(
            pw.repo_id,
            (SELECT id FROM git_repos WHERE repo_url = pp.repo_url LIMIT 1)
        )                                                   AS repo_id,
        COALESCE(gr.name,           pp.name)                AS name,
        COALESCE(gr.description,    pp.description)         AS description,
        pp.type,
        COALESCE(gr.status,         pp.status)              AS status,
        COALESCE(gr.priority,       pp.priority)            AS priority,
        COALESCE(gr.tech_stack,     pp.tech_stack)          AS tech_stack,
        pp.tags                                             AS tags,
        COALESCE(gr.repo_url,       pp.repo_url)            AS repo_url,
        COALESCE(gr.upstream_url,   pp.upstream_url)        AS upstream_url,
        COALESCE(gr.default_branch, pp.default_branch)      AS default_branch,
        COALESCE(gr.staging_url,    pp.staging_url)         AS staging_url,
        COALESCE(gr.prod_url,       pp.prod_url)            AS prod_url,
        COALESCE(gr.server_note,    pp.server_note)         AS server_note,
        pw.space_id,
        pw.wsl_path,
        pw.win_path,
        pw.platform,
        pw.recent_focus,
        pw.startup_commands,
        pw.last_push_at,
        pw.updated_at
    FROM project_workspaces pw
    JOIN project_profiles pp ON pp.id = pw.profile_id
    LEFT JOIN git_repos gr ON gr.id = pw.repo_id
";

#[derive(Debug, Deserialize)]
pub struct CreateProjectInput {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    #[serde(rename = "type")]
    pub type_: Option<String>,
    pub status: Option<String>,
    pub priority: Option<String>,
    // profile 字段（共享）
    pub tech_stack: Option<String>,
    pub repo_url: Option<String>,
    pub upstream_url: Option<String>,
    pub default_branch: Option<String>,
    pub staging_url: Option<String>,
    pub prod_url: Option<String>,
    pub server_note: Option<String>,
    // workspace 字段（每空间独立）
    pub space_id: Option<String>,
    pub wsl_path: Option<String>,
    pub win_path: Option<String>,
    pub platform: Option<String>,
    pub recent_focus: Option<String>,
    pub startup_commands: Option<String>,
    /// 若提供则跳过建 profile，直接为已有 profile 新增 workspace
    pub profile_id: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct UpdateProjectInput {
    // profile 字段（共享）
    pub name: Option<String>,
    pub description: Option<String>,
    #[serde(rename = "type")]
    pub type_: Option<String>,
    pub status: Option<String>,
    pub priority: Option<String>,
    pub tech_stack: Option<String>,
    pub tags: Option<String>,
    pub staging_url: Option<String>,
    pub prod_url: Option<String>,
    pub server_note: Option<String>,
    // workspace 字段（每空间独立）
    pub wsl_path: Option<String>,
    pub win_path: Option<String>,
    pub platform: Option<String>,
    pub recent_focus: Option<String>,
    pub startup_commands: Option<String>,
}

#[derive(Debug, Serialize)]
pub struct ProjectDetail {
    #[serde(flatten)]
    pub project: Project,
    pub recent_activities: Vec<ActivityLog>,
}

#[tauri::command]
pub fn get_projects(status: Option<String>, space_id: Option<String>) -> Result<Vec<Project>, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;

    let projects: Vec<Project> = match (&status, &space_id) {
        (Some(s), Some(sid)) => {
            let sql = format!("{PROJECT_VIEW_SQL} WHERE pp.status = ?1 AND pw.space_id = ?2 ORDER BY pw.updated_at DESC");
            let mut stmt = conn.prepare(&sql).map_err(|e| e.to_string())?;
            let x = stmt.query_map(params![s, sid], Project::from_row)
                .map_err(|e| e.to_string())?
                .map(|r| r.map_err(|e| e.to_string()))
                .collect::<Result<Vec<_>, _>>()?; x
        }
        (Some(s), None) => {
            let sql = format!("{PROJECT_VIEW_SQL} WHERE pp.status = ?1 ORDER BY pw.updated_at DESC");
            let mut stmt = conn.prepare(&sql).map_err(|e| e.to_string())?;
            let x = stmt.query_map(params![s], Project::from_row)
                .map_err(|e| e.to_string())?
                .map(|r| r.map_err(|e| e.to_string()))
                .collect::<Result<Vec<_>, _>>()?; x
        }
        (None, Some(sid)) => {
            let sql = format!("{PROJECT_VIEW_SQL} WHERE pw.space_id = ?1 ORDER BY pw.updated_at DESC");
            let mut stmt = conn.prepare(&sql).map_err(|e| e.to_string())?;
            let x = stmt.query_map(params![sid], Project::from_row)
                .map_err(|e| e.to_string())?
                .map(|r| r.map_err(|e| e.to_string()))
                .collect::<Result<Vec<_>, _>>()?; x
        }
        (None, None) => {
            let sql = format!("{PROJECT_VIEW_SQL} ORDER BY pw.updated_at DESC");
            let mut stmt = conn.prepare(&sql).map_err(|e| e.to_string())?;
            let x = stmt.query_map([], Project::from_row)
                .map_err(|e| e.to_string())?
                .map(|r| r.map_err(|e| e.to_string()))
                .collect::<Result<Vec<_>, _>>()?; x
        }
    };

    Ok(projects)
}

#[tauri::command]
pub fn get_project(id: String) -> Result<ProjectDetail, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let sql = format!("{PROJECT_VIEW_SQL} WHERE pw.id = ?1");
    let project = conn
        .query_row(&sql, params![id], Project::from_row)
        .map_err(|_| format!("Project not found: {id}"))?;
    let mut stmt = conn
        .prepare("SELECT * FROM activity_log WHERE project_id = ?1 ORDER BY created_at DESC LIMIT 5")
        .map_err(|e| e.to_string())?;
    let activities: Vec<ActivityLog> = stmt
        .query_map(params![id], ActivityLog::from_row)
        .map_err(|e| e.to_string())?
        .collect::<Result<Vec<_>, _>>()
        .map_err(|e| e.to_string())?;
    drop(stmt);
    Ok(ProjectDetail { project, recent_activities: activities })
}

#[tauri::command]
pub fn create_project(input: CreateProjectInput) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;

    let profile_id = if let Some(ref pid) = input.profile_id {
        // 快速路径：profile 已存在，直接新建 workspace
        pid.clone()
    } else {
        // 标准路径：先建 profile
        let pid = format!("prof-{}", input.id);
        conn.execute(
            "INSERT OR IGNORE INTO project_profiles
                (id, name, description, type, status, priority, tech_stack,
                 repo_url, upstream_url, default_branch,
                 staging_url, prod_url, server_note, updated_at)
             VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9,?10,?11,?12,?13,datetime('now'))",
            params![
                pid, input.name, input.description, input.type_,
                input.status.as_deref().unwrap_or("active"),
                input.priority.as_deref().unwrap_or("mid"),
                input.tech_stack, input.repo_url, input.upstream_url, input.default_branch,
                input.staging_url, input.prod_url, input.server_note,
            ],
        ).map_err(|e| e.to_string())?;
        pid
    };

    conn.execute(
        "INSERT OR IGNORE INTO project_workspaces
            (id, profile_id, space_id, wsl_path, win_path, platform,
             recent_focus, startup_commands, updated_at)
         VALUES (?1,?2,?3,?4,?5,?6,?7,?8,datetime('now'))",
        params![
            input.id, profile_id, input.space_id,
            input.wsl_path, input.win_path,
            input.platform.as_deref().unwrap_or("windows"),
            input.recent_focus, input.startup_commands,
        ],
    ).map_err(|e| {
        if e.to_string().contains("UNIQUE") { "Project id already exists".into() }
        else { e.to_string() }
    })?;

    Ok(())
}

#[tauri::command]
pub fn update_project(id: String, input: UpdateProjectInput) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;

    // 1. 更新 profile（通过 workspace.id 定位）
    conn.execute(
        "UPDATE project_profiles SET
            name           = COALESCE(?1, name),
            description    = COALESCE(?2, description),
            type           = COALESCE(?3, type),
            status         = COALESCE(?4, status),
            priority       = COALESCE(?5, priority),
            tech_stack     = COALESCE(?6, tech_stack),
            tags           = COALESCE(?7, tags),
            staging_url    = ?8,
            prod_url       = ?9,
            server_note    = ?10,
            updated_at     = datetime('now')
         WHERE id = (SELECT profile_id FROM project_workspaces WHERE id = ?11)",
        params![
            input.name, input.description, input.type_, input.status, input.priority,
            input.tech_stack, input.tags,
            input.staging_url, input.prod_url, input.server_note,
            id
        ],
    ).map_err(|e| e.to_string())?;

    // 2. 更新 workspace
    conn.execute(
        "UPDATE project_workspaces SET
            wsl_path         = COALESCE(?1, wsl_path),
            win_path         = COALESCE(?2, win_path),
            platform         = COALESCE(?3, platform),
            recent_focus     = COALESCE(?4, recent_focus),
            startup_commands = COALESCE(?5, startup_commands),
            updated_at       = datetime('now')
         WHERE id = ?6",
        params![
            input.wsl_path, input.win_path, input.platform,
            input.recent_focus, input.startup_commands,
            id
        ],
    ).map_err(|e| e.to_string())?;

    Ok(())
}

#[tauri::command]
pub fn update_project_tags(id: String, tags: String) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    conn.execute(
        "UPDATE project_profiles SET tags = ?1, updated_at = datetime('now')
         WHERE id = (SELECT profile_id FROM project_workspaces WHERE id = ?2)",
        params![if tags.is_empty() { None } else { Some(tags) }, id],
    ).map_err(|e| e.to_string())?;
    Ok(())
}

#[tauri::command]
pub fn delete_project(id: String, delete_local: Option<bool>) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;

    // 删除前先读取路径和 profile_id
    let (profile_id, win_path, wsl_path): (Option<String>, Option<String>, Option<String>) = conn
        .query_row(
            "SELECT profile_id, win_path, wsl_path FROM project_workspaces WHERE id = ?1",
            params![id],
            |row| Ok((row.get(0)?, row.get(1)?, row.get(2)?)),
        )
        .unwrap_or((None, None, None));

    // 清理产品组 MCP 配置（在删除 project_group_map 记录之前）
    let group_ids: Vec<String> = conn
        .prepare("SELECT group_id FROM project_group_map WHERE project_id = ?1")
        .map_err(|e| e.to_string())?
        .query_map(params![id], |row| row.get::<_, String>(0))
        .map_err(|e| e.to_string())?
        .filter_map(|r| r.ok())
        .collect();
    for group_id in &group_ids {
        if let Err(e) = mcp_inject::remove_project_mcp(&id, group_id) {
            eprintln!("[MCP remove] 删除项目时清除失败 project={} group={}: {}", id, group_id, e);
        }
    }

    // 清理关联数据（旧 FK 指向 projects 表，需手动清理）
    for table in &["activity_log", "project_tools", "project_group_map",
                   "project_env_tools", "deploy_commands"] {
        conn.execute(
            &format!("DELETE FROM {table} WHERE project_id = ?1"),
            params![id],
        ).map_err(|e| e.to_string())?;
    }
    conn.execute(
        "DELETE FROM relations WHERE from_project = ?1 OR to_project = ?1",
        params![id],
    ).map_err(|e| e.to_string())?;

    // 删除 workspace
    conn.execute("DELETE FROM project_workspaces WHERE id = ?1", params![id])
        .map_err(|e| e.to_string())?;

    // 若 profile 已无 workspace，同步删除 profile
    if let Some(pid) = profile_id {
        let remaining: i64 = conn.query_row(
            "SELECT COUNT(*) FROM project_workspaces WHERE profile_id = ?1",
            params![pid], |row| row.get(0),
        ).unwrap_or(0);
        if remaining == 0 {
            conn.execute("DELETE FROM project_profiles WHERE id = ?1", params![pid])
                .map_err(|e| e.to_string())?;
        }
    }

    // 删除本地文件夹（仅在明确要求时执行）
    if delete_local.unwrap_or(false) {
        // Windows 路径（直接用 std::fs）
        if let Some(path) = win_path.filter(|p| !p.is_empty()) {
            let p = std::path::Path::new(&path);
            if p.exists() {
                std::fs::remove_dir_all(p)
                    .map_err(|e| format!("删除本地文件夹失败: {e}"))?;
            }
        }
        // WSL 路径（通过 wsl.exe 删除）
        if let Some(path) = wsl_path.filter(|p| !p.is_empty()) {
            let _ = crate::silent_cmd("wsl")
                .args(["--", "rm", "-rf", &path])
                .output();
        }
    }

    Ok(())
}

/// 扫描指定目录，返回所有子文件夹信息 [{name, path}]
#[tauri::command]
pub fn scan_subfolders(dir: String) -> Result<Vec<Value>, String> {
    use std::fs;
    let entries = fs::read_dir(&dir).map_err(|e| format!("无法读取目录 \"{dir}\": {e}"))?;
    let mut result = Vec::new();
    for entry in entries.flatten() {
        let path = entry.path();
        if path.is_dir() {
            let name = entry.file_name().to_string_lossy().into_owned();
            if name.starts_with('.') { continue; }
            let mut obj = serde_json::Map::new();
            obj.insert("name".into(), Value::String(name));
            obj.insert("path".into(), Value::String(path.to_string_lossy().into_owned()));
            result.push(Value::Object(obj));
        }
    }
    result.sort_by(|a, b| {
        a.get("name").and_then(|v| v.as_str())
            .cmp(&b.get("name").and_then(|v| v.as_str()))
    });
    Ok(result)
}

#[derive(Debug, Deserialize)]
pub struct BatchProjectInput {
    pub id: String,
    pub name: String,
    pub win_path: Option<String>,
    pub wsl_path: Option<String>,
    pub status: String,
    pub priority: String,
    pub space_id: Option<String>,
    pub platform: Option<String>,
}

/// 批量创建项目（已存在的 workspace id 跳过）
#[tauri::command]
pub fn batch_create_projects(items: Vec<BatchProjectInput>) -> Result<Vec<String>, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let mut skipped = Vec::new();
    for item in items {
        let exists: bool = conn
            .query_row("SELECT 1 FROM project_workspaces WHERE id = ?1", params![item.id], |_| Ok(true))
            .unwrap_or(false);
        if exists { skipped.push(item.id); continue; }

        let profile_id = format!("prof-{}", item.id);
        conn.execute(
            "INSERT OR IGNORE INTO project_profiles (id, name, status, priority, updated_at)
             VALUES (?1, ?2, ?3, ?4, datetime('now'))",
            params![profile_id, item.name, item.status, item.priority],
        ).map_err(|e| e.to_string())?;

        conn.execute(
            "INSERT INTO project_workspaces
                (id, profile_id, win_path, wsl_path, space_id, platform, updated_at)
             VALUES (?1, ?2, ?3, ?4, ?5, ?6, datetime('now'))",
            params![
                item.id, profile_id, item.win_path, item.wsl_path,
                item.space_id, item.platform,
            ],
        ).map_err(|e| e.to_string())?;
    }
    Ok(skipped)
}
