use rusqlite::Connection;

use super::error::{LoreError, Result};
use super::path_resolver::escape_like;

pub fn resolve_project(conn: &Connection, project_str: &str) -> Result<i64> {
    let exact = conn.query_row(
        "SELECT id FROM projects WHERE path_with_namespace = ?1",
        rusqlite::params![project_str],
        |row| row.get::<_, i64>(0),
    );
    if let Ok(id) = exact {
        return Ok(id);
    }

    let ci = conn.query_row(
        "SELECT id FROM projects WHERE LOWER(path_with_namespace) = LOWER(?1)",
        rusqlite::params![project_str],
        |row| row.get::<_, i64>(0),
    );
    if let Ok(id) = ci {
        return Ok(id);
    }

    let escaped = escape_like(project_str);
    let mut suffix_stmt = conn.prepare(
        "SELECT id, path_with_namespace FROM projects
         WHERE path_with_namespace LIKE '%/' || ?1 ESCAPE '\\'
            OR path_with_namespace = ?2",
    )?;
    let suffix_matches: Vec<(i64, String)> = suffix_stmt
        .query_map(rusqlite::params![escaped, project_str], |row| {
            Ok((row.get(0)?, row.get(1)?))
        })?
        .collect::<std::result::Result<Vec<_>, _>>()?;

    match suffix_matches.len() {
        1 => return Ok(suffix_matches[0].0),
        n if n > 1 => {
            let matching: Vec<String> = suffix_matches.iter().map(|(_, p)| p.clone()).collect();
            return Err(LoreError::Ambiguous(format!(
                "Project '{}' is ambiguous. Matching projects:\n{}\n\nHint: Use the full path, e.g., --project={}",
                project_str,
                matching
                    .iter()
                    .map(|p| format!("  {}", p))
                    .collect::<Vec<_>>()
                    .join("\n"),
                matching[0]
            )));
        }
        _ => {}
    }

    let mut substr_stmt = conn.prepare(
        "SELECT id, path_with_namespace FROM projects
         WHERE LOWER(path_with_namespace) LIKE '%' || LOWER(?1) || '%' ESCAPE '\\'",
    )?;
    let substr_matches: Vec<(i64, String)> = substr_stmt
        .query_map(rusqlite::params![escaped], |row| {
            Ok((row.get(0)?, row.get(1)?))
        })?
        .collect::<std::result::Result<Vec<_>, _>>()?;

    match substr_matches.len() {
        1 => return Ok(substr_matches[0].0),
        n if n > 1 => {
            let matching: Vec<String> = substr_matches.iter().map(|(_, p)| p.clone()).collect();
            return Err(LoreError::Ambiguous(format!(
                "Project '{}' is ambiguous. Matching projects:\n{}\n\nHint: Use the full path, e.g., --project={}",
                project_str,
                matching
                    .iter()
                    .map(|p| format!("  {}", p))
                    .collect::<Vec<_>>()
                    .join("\n"),
                matching[0]
            )));
        }
        _ => {}
    }

    let mut all_stmt =
        conn.prepare("SELECT path_with_namespace FROM projects ORDER BY path_with_namespace")?;
    let all_projects: Vec<String> = all_stmt
        .query_map([], |row| row.get(0))?
        .collect::<std::result::Result<Vec<_>, _>>()?;

    if all_projects.is_empty() {
        return Err(LoreError::Other(format!(
            "Project '{}' not found. No projects have been synced yet.\n\nHint: Run 'lore ingest' first.",
            project_str
        )));
    }

    Err(LoreError::Other(format!(
        "Project '{}' not found.\n\nAvailable projects:\n{}\n\nHint: Use the full path, e.g., --project={}",
        project_str,
        all_projects
            .iter()
            .map(|p| format!("  {}", p))
            .collect::<Vec<_>>()
            .join("\n"),
        all_projects[0]
    )))
}

/// Escape LIKE metacharacters so `%` and `_` in user input are treated as
/// literals.  All queries using this must include `ESCAPE '\'`.
#[cfg(test)]
#[path = "project_tests.rs"]
mod tests;
