use std::collections::HashSet;
use std::collections::VecDeque;

use rusqlite::Connection;

use super::error::Result;

/// Resolves a file path through its rename history in `mr_file_changes`.
///
/// BFS in both directions: forward (`old_path` -> `new_path`) and backward
/// (`new_path` -> `old_path`). Returns all equivalent paths including the
/// original, sorted for determinism. Cycles are detected via a visited set.
///
/// `max_hops` limits the BFS depth (distance from the starting path).
pub fn resolve_rename_chain(
    conn: &Connection,
    project_id: i64,
    path: &str,
    max_hops: usize,
) -> Result<Vec<String>> {
    let mut visited: HashSet<String> = HashSet::new();
    visited.insert(path.to_string());

    if max_hops == 0 {
        return Ok(vec![path.to_string()]);
    }

    let mut queue: VecDeque<(String, usize)> = VecDeque::new();
    queue.push_back((path.to_string(), 0));

    let forward_sql = "\
        SELECT DISTINCT mfc.new_path FROM mr_file_changes mfc \
        WHERE mfc.project_id = ?1 AND mfc.old_path = ?2 AND mfc.change_type = 'renamed'";
    let backward_sql = "\
        SELECT DISTINCT mfc.old_path FROM mr_file_changes mfc \
        WHERE mfc.project_id = ?1 AND mfc.new_path = ?2 AND mfc.change_type = 'renamed'";

    while let Some((current, depth)) = queue.pop_front() {
        if depth >= max_hops {
            continue;
        }

        // Forward: current was the old name -> discover new names
        let mut fwd_stmt = conn.prepare_cached(forward_sql)?;
        let forward: Vec<String> = fwd_stmt
            .query_map(rusqlite::params![project_id, &current], |row| row.get(0))?
            .collect::<std::result::Result<Vec<_>, _>>()?;

        // Backward: current was the new name -> discover old names
        let mut bwd_stmt = conn.prepare_cached(backward_sql)?;
        let backward: Vec<String> = bwd_stmt
            .query_map(rusqlite::params![project_id, &current], |row| row.get(0))?
            .collect::<std::result::Result<Vec<_>, _>>()?;

        for discovered in forward.into_iter().chain(backward) {
            if visited.insert(discovered.clone()) {
                queue.push_back((discovered, depth + 1));
            }
        }
    }

    let mut paths: Vec<String> = visited.into_iter().collect();
    paths.sort();
    Ok(paths)
}

#[cfg(test)]
#[path = "file_history_tests.rs"]
mod tests;
