use crate::db::pool;
use rusqlite::params;
use serde::{Deserialize, Serialize};
use std::process::Command;

// ── 数据结构 ──────────────────────────────────────────────────────────────────

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct SoftwareCategory {
    pub id: String,
    pub name: String,
    pub color: String,
    pub sort_order: i32,
}

#[derive(Debug, Deserialize)]
pub struct SaveCategoryInput {
    pub name: String,
    pub color: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct GithubMirror {
    pub id: String,
    pub name: String,
    pub url_prefix: String,
    pub is_builtin: bool,
    pub sort_order: i32,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ServerNetworkConfig {
    pub server_id: String,
    pub github_mirror_id: Option<String>,
    pub last_tested: Option<String>,
    pub test_results: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ServerSoftware {
    pub id: String,
    pub name: String,
    pub description: Option<String>,
    pub category: Option<String>,
    pub install_type: String,
    pub install_cmd: Option<String>,
    pub github_url: Option<String>,
    pub remote_path: Option<String>,
    pub post_cmd: Option<String>,
    pub is_builtin: bool,
    pub sort_order: i32,
    pub created_at: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct SaveSoftwareInput {
    pub name: String,
    pub description: Option<String>,
    pub category: Option<String>,
    pub install_type: String,
    pub install_cmd: Option<String>,
    pub github_url: Option<String>,
    pub remote_path: Option<String>,
    pub post_cmd: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct SaveMirrorInput {
    pub name: String,
    pub url_prefix: String,
}

#[derive(Debug, Serialize)]
pub struct MirrorTestResult {
    pub mirror_id: String,
    pub reachable: bool,
    pub latency_ms: Option<u64>,
    pub error: Option<String>,
}

// ── 镜像 CRUD ─────────────────────────────────────────────────────────────────

#[tauri::command]
pub fn get_github_mirrors() -> Result<Vec<GithubMirror>, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let mut stmt = conn
        .prepare("SELECT id, name, url_prefix, is_builtin, sort_order FROM github_mirrors ORDER BY sort_order")
        .map_err(|e| e.to_string())?;
    let mirrors = stmt
        .query_map([], |row| {
            Ok(GithubMirror {
                id: row.get(0)?,
                name: row.get(1)?,
                url_prefix: row.get(2)?,
                is_builtin: row.get::<_, i64>(3)? != 0,
                sort_order: row.get(4)?,
            })
        })
        .map_err(|e| e.to_string())?
        .filter_map(|r| r.ok())
        .collect();
    Ok(mirrors)
}

#[tauri::command]
pub fn add_github_mirror(input: SaveMirrorInput) -> Result<GithubMirror, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let id = format!("mirror-{}", uuid::Uuid::new_v4());
    conn.execute(
        "INSERT INTO github_mirrors (id, name, url_prefix) VALUES (?1, ?2, ?3)",
        params![id, input.name, input.url_prefix],
    )
    .map_err(|e| e.to_string())?;
    Ok(GithubMirror {
        id,
        name: input.name,
        url_prefix: input.url_prefix,
        is_builtin: false,
        sort_order: 0,
    })
}

#[tauri::command]
pub fn delete_github_mirror(id: String) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    // 不允许删除内置镜像
    let builtin: bool = conn
        .query_row(
            "SELECT is_builtin FROM github_mirrors WHERE id = ?1",
            params![id],
            |row| row.get::<_, i64>(0).map(|v| v != 0),
        )
        .unwrap_or(false);
    if builtin {
        return Err("内置镜像不可删除".into());
    }
    conn.execute("DELETE FROM github_mirrors WHERE id = ?1", params![id])
        .map_err(|e| e.to_string())?;
    Ok(())
}

/// 本地测试镜像可用性（curl）
#[tauri::command]
pub fn test_mirror_local(mirror_id: String) -> Result<MirrorTestResult, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let (url_prefix,): (String,) = conn
        .query_row(
            "SELECT url_prefix FROM github_mirrors WHERE id = ?1",
            params![mirror_id],
            |row| Ok((row.get(0)?,)),
        )
        .map_err(|_| "镜像不存在".to_string())?;

    let test_url = if url_prefix.contains("github.com") {
        // 直连模式
        "https://github.com/robots.txt".to_string()
    } else {
        format!("{}/https://github.com/robots.txt", url_prefix.trim_end_matches('/'))
    };

    let start = std::time::Instant::now();
    let output = Command::new("curl")
        .args(["-sI", "--max-time", "8", &test_url])
        .output();

    match output {
        Ok(out) => {
            let elapsed = start.elapsed().as_millis() as u64;
            let stdout = String::from_utf8_lossy(&out.stdout);
            let first_line = stdout.lines().next().unwrap_or("");
            let reachable = first_line.contains("200") || first_line.contains("301") || first_line.contains("302");
            Ok(MirrorTestResult {
                mirror_id,
                reachable,
                latency_ms: Some(elapsed),
                error: if reachable { None } else { Some(first_line.to_string()) },
            })
        }
        Err(e) => Ok(MirrorTestResult {
            mirror_id,
            reachable: false,
            latency_ms: None,
            error: Some(e.to_string()),
        }),
    }
}

// ── 服务器网络配置 ────────────────────────────────────────────────────────────

#[tauri::command]
pub fn get_server_network_config(server_id: String) -> Result<Option<ServerNetworkConfig>, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    conn.query_row(
        "SELECT server_id, github_mirror_id, last_tested, test_results FROM server_network_config WHERE server_id = ?1",
        params![server_id],
        |row| {
            Ok(ServerNetworkConfig {
                server_id: row.get(0)?,
                github_mirror_id: row.get(1)?,
                last_tested: row.get(2)?,
                test_results: row.get(3)?,
            })
        },
    )
    .ok()
    .map(|c| Ok(Some(c)))
    .unwrap_or(Ok(None))
}

#[tauri::command]
pub fn set_server_mirror(server_id: String, mirror_id: Option<String>) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    conn.execute(
        "INSERT INTO server_network_config (server_id, github_mirror_id)
         VALUES (?1, ?2)
         ON CONFLICT(server_id) DO UPDATE SET github_mirror_id = ?2",
        params![server_id, mirror_id],
    )
    .map_err(|e| e.to_string())?;
    Ok(())
}

// ── 软件 CRUD ─────────────────────────────────────────────────────────────────

#[tauri::command]
pub fn get_server_software_list() -> Result<Vec<ServerSoftware>, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let mut stmt = conn
        .prepare(
            "SELECT id, name, description, category, install_type, install_cmd,
                    github_url, remote_path, post_cmd, is_builtin, sort_order, created_at
             FROM server_software ORDER BY sort_order, created_at",
        )
        .map_err(|e| e.to_string())?;
    let list = stmt
        .query_map([], |row| {
            Ok(ServerSoftware {
                id: row.get(0)?,
                name: row.get(1)?,
                description: row.get(2)?,
                category: row.get(3)?,
                install_type: row.get(4)?,
                install_cmd: row.get(5)?,
                github_url: row.get(6)?,
                remote_path: row.get(7)?,
                post_cmd: row.get(8)?,
                is_builtin: row.get::<_, i64>(9)? != 0,
                sort_order: row.get(10)?,
                created_at: row.get(11)?,
            })
        })
        .map_err(|e| e.to_string())?
        .filter_map(|r| r.ok())
        .collect();
    Ok(list)
}

#[tauri::command]
pub fn add_server_software(input: SaveSoftwareInput) -> Result<ServerSoftware, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let id = format!("sw-{}", uuid::Uuid::new_v4());
    conn.execute(
        "INSERT INTO server_software (id, name, description, category, install_type, install_cmd, github_url, remote_path, post_cmd)
         VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9)",
        params![
            id, input.name, input.description, input.category, input.install_type,
            input.install_cmd, input.github_url, input.remote_path, input.post_cmd,
        ],
    )
    .map_err(|e| e.to_string())?;
    Ok(ServerSoftware {
        id,
        name: input.name,
        description: input.description,
        category: input.category,
        install_type: input.install_type,
        install_cmd: input.install_cmd,
        github_url: input.github_url,
        remote_path: input.remote_path,
        post_cmd: input.post_cmd,
        is_builtin: false,
        sort_order: 0,
        created_at: None,
    })
}

#[tauri::command]
pub fn update_server_software(id: String, input: SaveSoftwareInput) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    conn.execute(
        "UPDATE server_software SET
            name = ?1, description = ?2, category = ?3, install_type = ?4,
            install_cmd = ?5, github_url = ?6, remote_path = ?7, post_cmd = ?8
         WHERE id = ?9",
        params![
            input.name, input.description, input.category, input.install_type,
            input.install_cmd, input.github_url, input.remote_path, input.post_cmd, id,
        ],
    )
    .map_err(|e| e.to_string())?;
    Ok(())
}

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

/// 为指定服务器生成某软件的安装命令（自动套镜像）
#[tauri::command]
pub fn generate_install_command(software_id: String, server_id: String) -> Result<Vec<String>, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;

    let sw: ServerSoftware = conn
        .query_row(
            "SELECT id, name, description, category, install_type, install_cmd,
                    github_url, remote_path, post_cmd, is_builtin, sort_order, created_at
             FROM server_software WHERE id = ?1",
            params![software_id],
            |row| {
                Ok(ServerSoftware {
                    id: row.get(0)?,
                    name: row.get(1)?,
                    description: row.get(2)?,
                    category: row.get(3)?,
                    install_type: row.get(4)?,
                    install_cmd: row.get(5)?,
                    github_url: row.get(6)?,
                    remote_path: row.get(7)?,
                    post_cmd: row.get(8)?,
                    is_builtin: row.get::<_, i64>(9)? != 0,
                    sort_order: row.get(10)?,
                    created_at: row.get(11)?,
                })
            },
        )
        .map_err(|_| "软件不存在".to_string())?;

    let mut commands: Vec<String> = Vec::new();

    match sw.install_type.as_str() {
        "package" => {
            if let Some(cmd) = &sw.install_cmd {
                commands.push(cmd.clone());
            }
        }
        "github" => {
            if let Some(github_url) = &sw.github_url {
                // 查该服务器的镜像配置
                let mirror_prefix: Option<String> = conn
                    .query_row(
                        "SELECT m.url_prefix FROM server_network_config c
                         JOIN github_mirrors m ON m.id = c.github_mirror_id
                         WHERE c.server_id = ?1",
                        params![server_id],
                        |row| row.get(0),
                    )
                    .ok();

                let download_url = if let Some(prefix) = mirror_prefix {
                    if prefix.contains("github.com") {
                        // 直连模式
                        github_url.clone()
                    } else {
                        format!("{}/{}", prefix.trim_end_matches('/'), github_url)
                    }
                } else {
                    github_url.clone()
                };

                let target = sw.remote_path.as_deref().unwrap_or("/tmp/download");
                commands.push(format!("wget \"{}\" -O {}", download_url, target));
            }
        }
        _ => {}
    }

    if let Some(post) = &sw.post_cmd {
        if !post.is_empty() {
            commands.push(post.clone());
        }
    }

    Ok(commands)
}

// ── 软件分类标签 CRUD ─────────────────────────────────────────────────────────

#[tauri::command]
pub fn get_software_categories() -> Result<Vec<SoftwareCategory>, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let mut stmt = conn
        .prepare("SELECT id, name, color, sort_order FROM software_categories ORDER BY sort_order")
        .map_err(|e| e.to_string())?;
    let list = stmt
        .query_map([], |row| {
            Ok(SoftwareCategory {
                id: row.get(0)?,
                name: row.get(1)?,
                color: row.get(2)?,
                sort_order: row.get(3)?,
            })
        })
        .map_err(|e| e.to_string())?
        .filter_map(|r| r.ok())
        .collect();
    Ok(list)
}

#[tauri::command]
pub fn add_software_category(input: SaveCategoryInput) -> Result<SoftwareCategory, String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    let id = format!("cat-{}", uuid::Uuid::new_v4());
    let color = input.color.unwrap_or_else(|| "#6b7280".to_string());
    let max_order: i32 = conn
        .query_row("SELECT COALESCE(MAX(sort_order), -1) FROM software_categories", [], |r| r.get(0))
        .unwrap_or(-1);
    conn.execute(
        "INSERT INTO software_categories (id, name, color, sort_order) VALUES (?1, ?2, ?3, ?4)",
        params![id, input.name, color, max_order + 1],
    )
    .map_err(|e| e.to_string())?;
    Ok(SoftwareCategory { id, name: input.name, color, sort_order: max_order + 1 })
}

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

#[tauri::command]
pub fn delete_software_category(id: String) -> Result<(), String> {
    let conn = pool().get().map_err(|e| e.to_string())?;
    // 将该分类下的软件改为 "custom"
    conn.execute(
        "UPDATE server_software SET category = 'cat-custom' WHERE category = ?1",
        params![id],
    )
    .map_err(|e| e.to_string())?;
    conn.execute("DELETE FROM software_categories WHERE id = ?1", params![id])
        .map_err(|e| e.to_string())?;
    Ok(())
}
