//! 数据库迁移 — 通用辅助(幂等迁移探测)

use rusqlite::Connection;

/// 检测表是否存在指定列（幂等迁移通用辅助）
pub(crate) fn column_exists(conn: &Connection, table: &str, col: &str) -> bool {
    let Ok(mut stmt) = conn.prepare(&format!("PRAGMA table_info({table})")) else { return false };
    let Ok(rows) = stmt.query_map([], |r| r.get::<_, String>(1)) else { return false };
    for r in rows {
        if let Ok(name) = r {
            if name == col { return true; }
        }
    }
    false
}

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

    // column_exists 是幂等迁移的通用探测辅助(V4+ 全部迁移靠它判断列存在性),
    // 本组用 in-memory SQLite(:memory:) 建临时表覆盖四种语义:
    //   列存在→true / 列不存在→false / 表不存在→false / 多列中正确匹配目标列名。

    /// 列存在返回 true:建表后探测真实列名。
    #[test]
    fn column_exists_returns_true_for_existing_column() {
        let conn = Connection::open_in_memory().expect("open in-memory db");
        conn.execute_batch(
            "CREATE TABLE probe_t (id TEXT PRIMARY KEY, name TEXT NOT NULL, score INTEGER);",
        )
        .expect("create probe table");
        assert!(column_exists(&conn, "probe_t", "id"));
        assert!(column_exists(&conn, "probe_t", "name"));
        assert!(column_exists(&conn, "probe_t", "score"));
    }

    /// 列不存在返回 false:表存在但目标列名未建。
    #[test]
    fn column_exists_returns_false_for_missing_column() {
        let conn = Connection::open_in_memory().expect("open in-memory db");
        conn.execute_batch("CREATE TABLE probe_t (id TEXT PRIMARY KEY, name TEXT);")
            .expect("create probe table");
        assert!(!column_exists(&conn, "probe_t", "archived"));
        assert!(!column_exists(&conn, "probe_t", "deleted_at"));
    }

    /// 表不存在返回 false:PRAGMA table_info 对未知表返回空结果集,函数应安全降级为 false。
    #[test]
    fn column_exists_returns_false_when_table_absent() {
        let conn = Connection::open_in_memory().expect("open in-memory db");
        // 不建任何表,直接探测
        assert!(!column_exists(&conn, "ghost_table", "any_col"));
    }

    /// 多列表中正确匹配目标列名(不误命中前缀/子串):验证精确比较非 contains。
    #[test]
    fn column_exists_matches_exact_name_among_many() {
        let conn = Connection::open_in_memory().expect("open in-memory db");
        conn.execute_batch(
            "CREATE TABLE probe_t (
                id TEXT PRIMARY KEY,
                prompt_tokens INTEGER,
                completion_tokens INTEGER,
                model TEXT
            );",
        )
        .expect("create probe table");
        // 命中真实列
        assert!(column_exists(&conn, "probe_t", "prompt_tokens"));
        assert!(column_exists(&conn, "probe_t", "completion_tokens"));
        // 不误命中子串(prefix/suffix 近似名)
        assert!(!column_exists(&conn, "probe_t", "tokens"));
        assert!(!column_exists(&conn, "probe_t", "prompt"));
        assert!(!column_exists(&conn, "probe_t", "model_configs"));
    }
}
