use super::AppEntry;

pub fn fuzzy_match(query: &str, text: &str) -> bool {
    let query_lower = query.to_lowercase();
    let text_lower = text.to_lowercase();

    text_lower.contains(&query_lower)
}

fn app_basename(app: &AppEntry) -> &str {
    app.app_id.strip_suffix(".desktop").unwrap_or(&app.app_id)
}

fn exec_name(app: &AppEntry) -> &str {
    let first = app.exec.split_whitespace().next().unwrap_or("");
    first.rsplit('/').next().unwrap_or(first)
}

fn matches(app: &AppEntry, query: &str) -> bool {
    fuzzy_match(query, &app.name)
        || fuzzy_match(query, app_basename(app))
        || fuzzy_match(query, exec_name(app))
        || app.startup_wm_class.as_deref().is_some_and(|c| fuzzy_match(query, c))
        || app.description.as_deref().is_some_and(|d| fuzzy_match(query, d))
}

pub fn score_app(app: &AppEntry, query: &str) -> i32 {
    let query_lower = query.to_lowercase();
    let name_lower = app.name.to_lowercase();
    let basename_lower = app_basename(app).to_lowercase();
    let exec_lower = exec_name(app).to_lowercase();
    let wm_class_lower = app.startup_wm_class.as_deref().map(str::to_lowercase);

    let mut score = 0;

    if name_lower == query_lower {
        score += 100;
    } else if name_lower.starts_with(&query_lower) {
        score += 50;
    } else if name_lower.contains(&query_lower) {
        score += 25;
    }

    if basename_lower == query_lower || exec_lower == query_lower
        || wm_class_lower.as_deref() == Some(query_lower.as_str())
    {
        score += 80;
    } else if basename_lower.starts_with(&query_lower)
        || exec_lower.starts_with(&query_lower)
        || wm_class_lower.as_deref().is_some_and(|c| c.starts_with(&query_lower))
    {
        score += 40;
    } else if basename_lower.contains(&query_lower)
        || exec_lower.contains(&query_lower)
        || wm_class_lower.as_deref().is_some_and(|c| c.contains(&query_lower))
    {
        score += 20;
    }

    if let Some(desc) = &app.description {
        if desc.to_lowercase().contains(&query_lower) {
            score += 10;
        }
    }

    score
}

pub fn search_apps(apps: &[AppEntry], query: &str) -> Vec<AppEntry> {
    if query.is_empty() {
        return apps.to_vec();
    }

    let mut results: Vec<(i32, AppEntry)> = apps
        .iter()
        .filter(|app| matches(app, query))
        .map(|app| (score_app(app, query), app.clone()))
        .collect();

    results.sort_by(|a, b| b.0.cmp(&a.0));

    results.into_iter().map(|(_, app)| app).collect()
}
