use rusqlite::{params, Connection};
use serde::{Deserialize, Serialize};

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

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Server {
    pub id: String,
    pub name: String,
    pub server_host: String,
    pub server_port: Option<String>,
    pub username: Option<String>,
    pub password: Option<String>,
    pub ssh_key_path: Option<String>,
    pub quick_links: Option<String>,
    pub notes: Option<String>,
    pub created_at: Option<String>,
    pub updated_at: Option<String>,
}

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct ProjectServerLink {
    pub project_id: String,
    pub server_id: String,
    pub role: Option<String>,
    pub deploy_type: Option<String>,
    pub deploy_path: Option<String>,
    pub container_name: Option<String>,
    pub start_command: Option<String>,
    pub notes: Option<String>,
    // 关联查询时附带的服务器信息
    pub server_name: Option<String>,
    pub server_host: Option<String>,
    pub server_port: Option<String>,
    pub username: Option<String>,
    pub password: Option<String>,
    pub ssh_key_path: Option<String>,
    pub quick_links: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct SaveServerInput {
    pub name: String,
    pub server_host: String,
    pub server_port: Option<String>,
    pub username: Option<String>,
    pub password: Option<String>,
    pub ssh_key_path: Option<String>,
    pub quick_links: Option<String>,
    pub notes: Option<String>,
}

#[derive(Debug, Deserialize)]
pub struct LinkServerInput {
    pub role: Option<String>,
    pub deploy_type: Option<String>,
    pub deploy_path: Option<String>,
    pub container_name: Option<String>,
    pub start_command: Option<String>,
    pub notes: Option<String>,
}

// ── 业务函数 ──────────────────────────────────────────────────────────────────

pub fn list_servers(conn: &Connection) -> Vec<Server> {
    let mut stmt = match conn.prepare(
        "SELECT id, name, server_host, server_port, username, password,
                ssh_key_path, quick_links, notes, created_at, updated_at
         FROM servers ORDER BY updated_at DESC",
    ) {
        Ok(s) => s,
        Err(_) => return vec![],
    };
    stmt.query_map([], |row| {
        Ok(Server {
            id:           row.get(0)?,
            name:         row.get(1)?,
            server_host:  row.get(2)?,
            server_port:  row.get(3)?,
            username:     row.get(4)?,
            password:     row.get(5)?,
            ssh_key_path: row.get(6)?,
            quick_links:  row.get(7)?,
            notes:        row.get(8)?,
            created_at:   row.get(9)?,
            updated_at:   row.get(10)?,
        })
    })
    .map(|iter| iter.filter_map(|r| r.ok()).collect())
    .unwrap_or_default()
}

pub fn create_server(conn: &Connection, input: &SaveServerInput) -> Result<Server, String> {
    let id = format!("srv-{}", uuid::Uuid::new_v4());
    conn.execute(
        "INSERT INTO servers (id, name, server_host, server_port, username, password,
                              ssh_key_path, quick_links, notes)
         VALUES (?1,?2,?3,?4,?5,?6,?7,?8,?9)",
        params![
            id, input.name, input.server_host, input.server_port,
            input.username, input.password, input.ssh_key_path,
            input.quick_links, input.notes,
        ],
    )
    .map_err(|e| e.to_string())?;
    get_server(conn, &id).ok_or_else(|| "创建后查询失败".into())
}

pub fn get_server(conn: &Connection, id: &str) -> Option<Server> {
    conn.query_row(
        "SELECT id, name, server_host, server_port, username, password,
                ssh_key_path, quick_links, notes, created_at, updated_at
         FROM servers WHERE id = ?1",
        params![id],
        |row| {
            Ok(Server {
                id:           row.get(0)?,
                name:         row.get(1)?,
                server_host:  row.get(2)?,
                server_port:  row.get(3)?,
                username:     row.get(4)?,
                password:     row.get(5)?,
                ssh_key_path: row.get(6)?,
                quick_links:  row.get(7)?,
                notes:        row.get(8)?,
                created_at:   row.get(9)?,
                updated_at:   row.get(10)?,
            })
        },
    )
    .ok()
}

pub fn update_server(conn: &Connection, id: &str, input: &SaveServerInput) -> Result<Server, String> {
    let rows = conn.execute(
        "UPDATE servers SET
            name = ?1, server_host = ?2, server_port = ?3, username = ?4,
            password = ?5, ssh_key_path = ?6, quick_links = ?7, notes = ?8,
            updated_at = datetime('now')
         WHERE id = ?9",
        params![
            input.name, input.server_host, input.server_port, input.username,
            input.password, input.ssh_key_path, input.quick_links, input.notes,
            id,
        ],
    )
    .map_err(|e| e.to_string())?;
    if rows == 0 {
        return Err(format!("服务器 {} 不存在", id));
    }
    get_server(conn, id).ok_or_else(|| "更新后查询失败".into())
}

pub fn delete_server_fn(conn: &Connection, id: &str) -> Result<(), String> {
    conn.execute("DELETE FROM project_server_map WHERE server_id = ?1", params![id])
        .map_err(|e| e.to_string())?;
    // 级联删除服务连接配置 + 宿主机应用
    let _ = super::service_config::delete_configs_by_server(conn, id);
    let _ = super::host_apps::delete_apps_by_server(conn, id);
    conn.execute("DELETE FROM servers WHERE id = ?1", params![id])
        .map_err(|e| e.to_string())?;
    Ok(())
}

// ── 关联管理 ──────────────────────────────────────────────────────────────────

pub fn link_project_server(
    conn: &Connection,
    project_id: &str,
    server_id: &str,
    input: &LinkServerInput,
) -> Result<(), String> {
    conn.execute(
        "INSERT INTO project_server_map
            (project_id, server_id, role, deploy_type, deploy_path, container_name, start_command, notes)
         VALUES (?1, ?2, ?3, ?4, ?5, ?6, ?7, ?8)
         ON CONFLICT(project_id, server_id) DO UPDATE SET
            role = ?3, deploy_type = ?4, deploy_path = ?5,
            container_name = ?6, start_command = ?7, notes = ?8",
        params![
            project_id, server_id, input.role, input.deploy_type,
            input.deploy_path, input.container_name, input.start_command, input.notes,
        ],
    )
    .map_err(|e| e.to_string())?;
    Ok(())
}

pub fn unlink_project_server(
    conn: &Connection,
    project_id: &str,
    server_id: &str,
) -> Result<(), String> {
    conn.execute(
        "DELETE FROM project_server_map WHERE project_id = ?1 AND server_id = ?2",
        params![project_id, server_id],
    )
    .map_err(|e| e.to_string())?;
    Ok(())
}

const LINK_SELECT_SQL: &str =
    "SELECT m.project_id, m.server_id, m.role,
            m.deploy_type, m.deploy_path, m.container_name, m.start_command, m.notes,
            s.name, s.server_host, s.server_port, s.username, s.password,
            s.ssh_key_path, s.quick_links
     FROM project_server_map m
     JOIN servers s ON s.id = m.server_id";

fn link_from_row(row: &rusqlite::Row<'_>) -> rusqlite::Result<ProjectServerLink> {
    Ok(ProjectServerLink {
        project_id:     row.get(0)?,
        server_id:      row.get(1)?,
        role:           row.get(2)?,
        deploy_type:    row.get(3)?,
        deploy_path:    row.get(4)?,
        container_name: row.get(5)?,
        start_command:  row.get(6)?,
        notes:          row.get(7)?,
        server_name:    row.get(8)?,
        server_host:    row.get(9)?,
        server_port:    row.get(10)?,
        username:       row.get(11)?,
        password:       row.get(12)?,
        ssh_key_path:   row.get(13)?,
        quick_links:    row.get(14)?,
    })
}

/// 获取某个项目关联的所有服务器（含服务器详细信息）
pub fn get_project_servers(conn: &Connection, project_id: &str) -> Vec<ProjectServerLink> {
    let sql = format!("{LINK_SELECT_SQL} WHERE m.project_id = ?1");
    let mut stmt = match conn.prepare(&sql) {
        Ok(s) => s,
        Err(_) => return vec![],
    };
    stmt.query_map(params![project_id], link_from_row)
        .map(|iter| iter.filter_map(|r| r.ok()).collect())
        .unwrap_or_default()
}

/// 获取某台服务器关联的所有项目
pub fn get_server_projects(conn: &Connection, server_id: &str) -> Vec<ProjectServerLink> {
    let sql = format!("{LINK_SELECT_SQL} WHERE m.server_id = ?1");
    let mut stmt = match conn.prepare(&sql) {
        Ok(s) => s,
        Err(_) => return vec![],
    };
    stmt.query_map(params![server_id], link_from_row)
        .map(|iter| iter.filter_map(|r| r.ok()).collect())
        .unwrap_or_default()
}

// ── Tauri command 薄包装层 ────────────────────────────────────────────────────

#[tauri::command]
pub fn get_servers() -> Result<Vec<Server>, String> {
    let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
    Ok(list_servers(&conn))
}

#[tauri::command]
pub fn add_server(input: SaveServerInput) -> Result<Server, String> {
    let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
    create_server(&conn, &input)
}

#[tauri::command]
pub fn edit_server(id: String, input: SaveServerInput) -> Result<Server, String> {
    let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
    update_server(&conn, &id, &input)
}

#[tauri::command]
pub fn delete_server(id: String) -> Result<(), String> {
    let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
    delete_server_fn(&conn, &id)
}

#[tauri::command]
pub fn link_server(project_id: String, server_id: String, input: LinkServerInput) -> Result<(), String> {
    let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
    link_project_server(&conn, &project_id, &server_id, &input)
}

#[tauri::command]
pub fn unlink_server(project_id: String, server_id: String) -> Result<(), String> {
    let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
    unlink_project_server(&conn, &project_id, &server_id)
}

#[tauri::command]
pub fn get_project_server_links(project_id: String) -> Result<Vec<ProjectServerLink>, String> {
    let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
    Ok(get_project_servers(&conn, &project_id))
}

#[tauri::command]
pub fn get_server_project_links(server_id: String) -> Result<Vec<ProjectServerLink>, String> {
    let conn = crate::db::pool().get().map_err(|e| e.to_string())?;
    Ok(get_server_projects(&conn, &server_id))
}

// ── 单元测试 ──────────────────────────────────────────────────────────────────

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

    fn setup() -> Connection {
        let conn = Connection::open_in_memory().unwrap();
        conn.execute_batch(
            "CREATE TABLE servers (
                id TEXT PRIMARY KEY, name TEXT NOT NULL, server_host TEXT NOT NULL,
                server_port TEXT DEFAULT '22', username TEXT, password TEXT,
                ssh_key_path TEXT, quick_links TEXT, notes TEXT,
                created_at TEXT DEFAULT (datetime('now')),
                updated_at TEXT DEFAULT (datetime('now'))
            );
            CREATE TABLE project_server_map (
                project_id TEXT NOT NULL, server_id TEXT NOT NULL,
                role TEXT DEFAULT 'production', deploy_type TEXT DEFAULT 'bare',
                deploy_path TEXT, container_name TEXT, start_command TEXT, notes TEXT,
                PRIMARY KEY (project_id, server_id)
            );",
        )
        .unwrap();
        conn
    }

    #[test]
    fn test_crud_server() {
        let conn = setup();
        let input = SaveServerInput {
            name: "测试服务器".into(),
            server_host: "192.168.1.100".into(),
            server_port: Some("22".into()),
            username: Some("root".into()),
            password: Some("secret".into()),
            ssh_key_path: None,
            quick_links: None,
            notes: None,
        };
        let srv = create_server(&conn, &input).unwrap();
        assert_eq!(srv.name, "测试服务器");
        assert_eq!(srv.server_host, "192.168.1.100");

        let all = list_servers(&conn);
        assert_eq!(all.len(), 1);

        let update = SaveServerInput {
            name: "生产服务器".into(),
            server_host: "10.0.0.1".into(),
            server_port: Some("2222".into()),
            username: Some("deploy".into()),
            password: None,
            ssh_key_path: None,
            quick_links: None,
            notes: Some("已更新".into()),
        };
        let updated = update_server(&conn, &srv.id, &update).unwrap();
        assert_eq!(updated.name, "生产服务器");
        assert_eq!(updated.server_host, "10.0.0.1");

        delete_server_fn(&conn, &srv.id).unwrap();
        assert!(list_servers(&conn).is_empty());
    }

    #[test]
    fn test_link_and_query() {
        let conn = setup();
        let input = SaveServerInput {
            name: "Web Server".into(),
            server_host: "1.2.3.4".into(),
            server_port: Some("22".into()),
            username: Some("ubuntu".into()),
            password: None,
            ssh_key_path: None,
            quick_links: None,
            notes: None,
        };
        let srv = create_server(&conn, &input).unwrap();

        let link = LinkServerInput {
            role: Some("production".into()),
            deploy_type: None,
            deploy_path: Some("/var/www/app".into()),
            container_name: None,
            start_command: None,
            notes: None,
        };
        link_project_server(&conn, "proj-1", &srv.id, &link).unwrap();
        link_project_server(&conn, "proj-2", &srv.id, &LinkServerInput {
            role: Some("staging".into()),
            deploy_type: None,
            deploy_path: Some("/var/www/staging".into()),
            container_name: None,
            start_command: None,
            notes: None,
        }).unwrap();

        let project_links = get_project_servers(&conn, "proj-1");
        assert_eq!(project_links.len(), 1);
        assert_eq!(project_links[0].server_name.as_deref(), Some("Web Server"));
        assert_eq!(project_links[0].deploy_path.as_deref(), Some("/var/www/app"));

        let server_links = get_server_projects(&conn, &srv.id);
        assert_eq!(server_links.len(), 2);

        unlink_project_server(&conn, "proj-1", &srv.id).unwrap();
    }

    #[test]
    fn test_delete_server_cascades_links() {
        let conn = setup();
        let srv = create_server(&conn, &SaveServerInput {
            name: "Temp".into(), server_host: "1.1.1.1".into(),
            server_port: None, username: None, password: None,
            ssh_key_path: None, quick_links: None, notes: None,
        }).unwrap();
        link_project_server(&conn, "proj-1", &srv.id, &LinkServerInput {
            role: None, deploy_type: None, deploy_path: None,
            container_name: None, start_command: None, notes: None,
        }).unwrap();
        delete_server_fn(&conn, &srv.id).unwrap();
        assert!(get_project_servers(&conn, "proj-1").is_empty());
    }
}
