use crate::db;
use rusqlite::params;
use serde::{Deserialize, Serialize};
use uuid::Uuid;

#[derive(Serialize, Deserialize, Clone)]
pub struct ProjectTag {
    pub id: String,
    pub name: String,
    pub color: String,
}

#[derive(Serialize)]
pub struct ProfileTagMap {
    pub profile_id: String,
    pub tag_id: String,
}

#[tauri::command]
pub fn get_tags() -> Result<Vec<ProjectTag>, String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;
    let mut stmt = conn.prepare(
        "SELECT id, name, color FROM project_tags ORDER BY name"
    ).map_err(|e| e.to_string())?;
    let tags = stmt.query_map([], |row| Ok(ProjectTag {
        id: row.get(0)?,
        name: row.get(1)?,
        color: row.get(2)?,
    }))
    .map_err(|e| e.to_string())?
    .collect::<Result<Vec<_>, _>>()
    .map_err(|e| e.to_string())?;
    Ok(tags)
}

#[tauri::command]
pub fn create_tag(name: String, color: String) -> Result<String, String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;
    let id = Uuid::new_v4().to_string();
    conn.execute(
        "INSERT INTO project_tags (id, name, color) VALUES (?1, ?2, ?3)",
        params![id, name.trim(), color],
    ).map_err(|e| e.to_string())?;
    Ok(id)
}

#[tauri::command]
pub fn update_tag(id: String, name: String, color: String) -> Result<(), String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;
    conn.execute(
        "UPDATE project_tags SET name = ?1, color = ?2 WHERE id = ?3",
        params![name.trim(), color, id],
    ).map_err(|e| e.to_string())?;
    Ok(())
}

#[tauri::command]
pub fn delete_tag(id: String) -> Result<(), String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;
    conn.execute("DELETE FROM project_tag_map WHERE tag_id = ?1", params![id])
        .map_err(|e| e.to_string())?;
    conn.execute("DELETE FROM project_tags WHERE id = ?1", params![id])
        .map_err(|e| e.to_string())?;
    Ok(())
}

/// 获取某个 workspace 对应项目的标签
#[tauri::command]
pub fn get_project_tags(workspace_id: String) -> Result<Vec<ProjectTag>, String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;
    let mut stmt = conn.prepare(
        "SELECT t.id, t.name, t.color
         FROM project_tags t
         JOIN project_tag_map m ON m.tag_id = t.id
         JOIN project_workspaces pw ON pw.profile_id = m.profile_id
         WHERE pw.id = ?1
         ORDER BY t.name"
    ).map_err(|e| e.to_string())?;
    let tags = stmt.query_map(params![workspace_id], |row| Ok(ProjectTag {
        id: row.get(0)?,
        name: row.get(1)?,
        color: row.get(2)?,
    }))
    .map_err(|e| e.to_string())?
    .collect::<Result<Vec<_>, _>>()
    .map_err(|e| e.to_string())?;
    Ok(tags)
}

/// 全量替换某个 workspace 对应项目的标签（tag_ids 为空则清空）
#[tauri::command]
pub fn set_project_tags(workspace_id: String, tag_ids: Vec<String>) -> Result<(), String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;
    // 找到 profile_id
    let profile_id: String = conn.query_row(
        "SELECT profile_id FROM project_workspaces WHERE id = ?1",
        params![workspace_id],
        |row| row.get(0),
    ).map_err(|e| format!("workspace not found: {e}"))?;

    conn.execute("DELETE FROM project_tag_map WHERE profile_id = ?1", params![profile_id])
        .map_err(|e| e.to_string())?;
    for tag_id in &tag_ids {
        conn.execute(
            "INSERT OR IGNORE INTO project_tag_map (profile_id, tag_id) VALUES (?1, ?2)",
            params![profile_id, tag_id],
        ).map_err(|e| e.to_string())?;
    }
    Ok(())
}

/// 获取全量 project_tag_map（用于前端 join）
#[tauri::command]
pub fn get_all_project_tag_maps() -> Result<Vec<ProfileTagMap>, String> {
    let conn = db::pool().get().map_err(|e| e.to_string())?;
    let mut stmt = conn.prepare(
        "SELECT profile_id, tag_id FROM project_tag_map"
    ).map_err(|e| e.to_string())?;
    let maps = stmt.query_map([], |row| Ok(ProfileTagMap {
        profile_id: row.get(0)?,
        tag_id: row.get(1)?,
    }))
    .map_err(|e| e.to_string())?
    .collect::<Result<Vec<_>, _>>()
    .map_err(|e| e.to_string())?;
    Ok(maps)
}
