#![allow(dead_code)]

//! Trace screen actions — fetch file provenance chains from the local database.
//!
//! Wraps `run_trace()` from `lore::core::trace` and provides an autocomplete
//! path query for the input field.

use anyhow::Result;
use rusqlite::Connection;

use lore::core::trace::{self, TraceResult};

/// Default limit for trace chain results in TUI queries.
const DEFAULT_LIMIT: usize = 50;

/// Fetch trace chains for a file path.
///
/// Wraps [`trace::run_trace()`] with TUI defaults.
pub fn fetch_trace(
    conn: &Connection,
    project_id: Option<i64>,
    path: &str,
    follow_renames: bool,
    include_discussions: bool,
) -> Result<TraceResult> {
    Ok(trace::run_trace(
        conn,
        project_id,
        path,
        follow_renames,
        include_discussions,
        DEFAULT_LIMIT,
    )?)
}

/// Fetch known file paths from `mr_file_changes` for autocomplete.
///
/// Returns distinct `new_path` values scoped to the given project (or all
/// projects if `None`), sorted alphabetically.
pub fn fetch_known_paths(conn: &Connection, project_id: Option<i64>) -> Result<Vec<String>> {
    let paths = if let Some(pid) = project_id {
        let mut stmt = conn.prepare(
            "SELECT DISTINCT new_path FROM mr_file_changes WHERE project_id = ?1 ORDER BY new_path",
        )?;
        let rows = stmt.query_map([pid], |row| row.get::<_, String>(0))?;
        rows.collect::<std::result::Result<Vec<_>, _>>()?
    } else {
        let mut stmt =
            conn.prepare("SELECT DISTINCT new_path FROM mr_file_changes ORDER BY new_path")?;
        let rows = stmt.query_map([], |row| row.get::<_, String>(0))?;
        rows.collect::<std::result::Result<Vec<_>, _>>()?
    };
    Ok(paths)
}

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

    /// Minimal schema for trace queries.
    fn create_trace_schema(conn: &Connection) {
        conn.execute_batch(
            "
            CREATE TABLE projects (
                id INTEGER PRIMARY KEY,
                gitlab_project_id INTEGER UNIQUE NOT NULL,
                path_with_namespace TEXT NOT NULL
            );
            CREATE TABLE merge_requests (
                id INTEGER PRIMARY KEY,
                gitlab_id INTEGER UNIQUE NOT NULL,
                project_id INTEGER NOT NULL,
                iid INTEGER NOT NULL,
                title TEXT,
                state TEXT,
                author_username TEXT,
                draft INTEGER NOT NULL DEFAULT 0,
                created_at INTEGER,
                updated_at INTEGER,
                merged_at INTEGER,
                closed_at INTEGER,
                web_url TEXT,
                last_seen_at INTEGER NOT NULL
            );
            CREATE TABLE mr_file_changes (
                id INTEGER PRIMARY KEY,
                merge_request_id INTEGER NOT NULL,
                project_id INTEGER NOT NULL,
                new_path TEXT NOT NULL,
                old_path TEXT,
                change_type TEXT NOT NULL
            );
            CREATE TABLE entity_references (
                id INTEGER PRIMARY KEY,
                source_entity_type TEXT NOT NULL,
                source_entity_id INTEGER NOT NULL,
                target_entity_type TEXT NOT NULL,
                target_entity_id INTEGER,
                target_iid INTEGER NOT NULL,
                project_id INTEGER NOT NULL,
                reference_type TEXT NOT NULL
            );
            CREATE TABLE issues (
                id INTEGER PRIMARY KEY,
                gitlab_id INTEGER UNIQUE NOT NULL,
                project_id INTEGER NOT NULL,
                iid INTEGER NOT NULL,
                title TEXT,
                state TEXT NOT NULL,
                author_username TEXT,
                web_url TEXT,
                created_at INTEGER NOT NULL,
                updated_at INTEGER NOT NULL,
                last_seen_at INTEGER NOT NULL
            );
            CREATE TABLE discussions (
                id INTEGER PRIMARY KEY,
                gitlab_discussion_id TEXT NOT NULL,
                project_id INTEGER NOT NULL,
                noteable_type TEXT NOT NULL,
                issue_id INTEGER,
                merge_request_id INTEGER,
                resolvable INTEGER NOT NULL DEFAULT 0,
                resolved INTEGER NOT NULL DEFAULT 0,
                last_note_at INTEGER NOT NULL DEFAULT 0,
                last_seen_at INTEGER NOT NULL
            );
            CREATE TABLE notes (
                id INTEGER PRIMARY KEY,
                gitlab_id INTEGER UNIQUE NOT NULL,
                discussion_id INTEGER NOT NULL,
                project_id INTEGER NOT NULL,
                is_system INTEGER NOT NULL DEFAULT 0,
                author_username TEXT,
                body TEXT,
                note_type TEXT,
                position_new_path TEXT,
                position_old_path TEXT,
                created_at INTEGER NOT NULL,
                updated_at INTEGER NOT NULL,
                last_seen_at INTEGER NOT NULL
            );
            CREATE INDEX idx_mfc_new_path_project_mr
                ON mr_file_changes(new_path, project_id, merge_request_id);
            CREATE INDEX idx_mfc_old_path_project_mr
                ON mr_file_changes(old_path, project_id, merge_request_id);
            ",
        )
        .expect("create trace schema");
    }

    #[test]
    fn test_fetch_trace_empty_db() {
        let conn = Connection::open_in_memory().unwrap();
        create_trace_schema(&conn);

        let result = fetch_trace(&conn, None, "src/main.rs", true, true).unwrap();
        assert!(result.trace_chains.is_empty());
        assert_eq!(result.total_chains, 0);
    }

    #[test]
    fn test_fetch_trace_with_mr() {
        let conn = Connection::open_in_memory().unwrap();
        create_trace_schema(&conn);

        // Insert a project, MR, and file change.
        conn.execute_batch(
            "
            INSERT INTO projects(id, gitlab_project_id, path_with_namespace)
            VALUES (1, 100, 'group/project');
            INSERT INTO merge_requests(id, gitlab_id, project_id, iid, title, state, author_username, updated_at, last_seen_at)
            VALUES (1, 200, 1, 42, 'Add main.rs', 'merged', 'alice', 1700000000000, 1700000000000);
            INSERT INTO mr_file_changes(id, merge_request_id, project_id, new_path, change_type)
            VALUES (1, 1, 1, 'src/main.rs', 'added');
            ",
        )
        .unwrap();

        let result = fetch_trace(&conn, Some(1), "src/main.rs", true, false).unwrap();
        assert_eq!(result.trace_chains.len(), 1);
        assert_eq!(result.trace_chains[0].mr_iid, 42);
        assert_eq!(result.trace_chains[0].mr_author, "alice");
        assert_eq!(result.trace_chains[0].change_type, "added");
    }

    #[test]
    fn test_fetch_known_paths_empty() {
        let conn = Connection::open_in_memory().unwrap();
        create_trace_schema(&conn);

        let paths = fetch_known_paths(&conn, None).unwrap();
        assert!(paths.is_empty());
    }

    #[test]
    fn test_fetch_known_paths_with_data() {
        let conn = Connection::open_in_memory().unwrap();
        create_trace_schema(&conn);

        conn.execute_batch(
            "
            INSERT INTO mr_file_changes(id, merge_request_id, project_id, new_path, change_type)
            VALUES (1, 1, 1, 'src/b.rs', 'added'),
                   (2, 1, 1, 'src/a.rs', 'modified'),
                   (3, 2, 1, 'src/b.rs', 'modified');
            ",
        )
        .unwrap();

        let paths = fetch_known_paths(&conn, None).unwrap();
        assert_eq!(paths, vec!["src/a.rs", "src/b.rs"]);
    }

    #[test]
    fn test_fetch_known_paths_scoped_to_project() {
        let conn = Connection::open_in_memory().unwrap();
        create_trace_schema(&conn);

        conn.execute_batch(
            "
            INSERT INTO mr_file_changes(id, merge_request_id, project_id, new_path, change_type)
            VALUES (1, 1, 1, 'src/a.rs', 'added'),
                   (2, 2, 2, 'src/b.rs', 'added');
            ",
        )
        .unwrap();

        let paths = fetch_known_paths(&conn, Some(1)).unwrap();
        assert_eq!(paths, vec!["src/a.rs"]);
    }
}
