#![allow(dead_code)]

use anyhow::{Context, Result};
use rusqlite::Connection;

use crate::state::bootstrap::{DataReadiness, SchemaCheck};

/// Minimum schema version required by this TUI version.
pub const MINIMUM_SCHEMA_VERSION: i32 = 20;

/// Check the schema version of the database.
///
/// Returns [`SchemaCheck::NoDB`] if the `schema_version` table doesn't exist,
/// [`SchemaCheck::Incompatible`] if the version is below the minimum,
/// or [`SchemaCheck::Compatible`] if all is well.
pub fn check_schema_version(conn: &Connection, minimum: i32) -> SchemaCheck {
    // Check if schema_version table exists.
    let table_exists: bool = conn
        .query_row(
            "SELECT COUNT(*) FROM sqlite_master WHERE type='table' AND name='schema_version'",
            [],
            |r| r.get::<_, i64>(0),
        )
        .map(|c| c > 0)
        .unwrap_or(false);

    if !table_exists {
        return SchemaCheck::NoDB;
    }

    // Read the current version.
    match conn.query_row("SELECT version FROM schema_version LIMIT 1", [], |r| {
        r.get::<_, i32>(0)
    }) {
        Ok(version) if version >= minimum => SchemaCheck::Compatible { version },
        Ok(found) => SchemaCheck::Incompatible { found, minimum },
        Err(_) => SchemaCheck::NoDB,
    }
}

/// Check whether the database has enough data to skip the bootstrap screen.
///
/// Counts issues, merge requests, and search documents. The `documents` table
/// may not exist on older schemas, so its absence is treated as "no documents."
pub fn check_data_readiness(conn: &Connection) -> Result<DataReadiness> {
    let has_issues: bool = conn
        .query_row("SELECT EXISTS(SELECT 1 FROM issues LIMIT 1)", [], |r| {
            r.get(0)
        })
        .context("checking issues")?;

    let has_mrs: bool = conn
        .query_row(
            "SELECT EXISTS(SELECT 1 FROM merge_requests LIMIT 1)",
            [],
            |r| r.get(0),
        )
        .context("checking merge requests")?;

    // documents table may not exist yet (created by generate-docs).
    let has_documents: bool = conn
        .query_row("SELECT EXISTS(SELECT 1 FROM documents LIMIT 1)", [], |r| {
            r.get(0)
        })
        .unwrap_or(false);

    let schema_version = conn
        .query_row("SELECT version FROM schema_version LIMIT 1", [], |r| {
            r.get::<_, i32>(0)
        })
        .unwrap_or(0);

    Ok(DataReadiness {
        has_issues,
        has_mrs,
        has_documents,
        schema_version,
    })
}

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

    /// Create the minimal schema needed for bootstrap / data-readiness queries.
    fn create_dashboard_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 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,
                created_at INTEGER NOT NULL,
                updated_at INTEGER NOT NULL,
                last_seen_at INTEGER 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,
                created_at INTEGER,
                updated_at INTEGER,
                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,
                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,
                created_at INTEGER NOT NULL,
                updated_at INTEGER NOT NULL,
                last_seen_at INTEGER NOT NULL
            );
            CREATE TABLE documents (
                id INTEGER PRIMARY KEY,
                source_type TEXT NOT NULL,
                source_id INTEGER NOT NULL,
                project_id INTEGER NOT NULL,
                content_text TEXT NOT NULL,
                content_hash TEXT NOT NULL
            );
            CREATE TABLE embedding_metadata (
                document_id INTEGER NOT NULL,
                chunk_index INTEGER NOT NULL DEFAULT 0,
                model TEXT NOT NULL,
                dims INTEGER NOT NULL,
                document_hash TEXT NOT NULL,
                chunk_hash TEXT NOT NULL,
                created_at INTEGER NOT NULL,
                PRIMARY KEY(document_id, chunk_index)
            );
            CREATE TABLE sync_runs (
                id INTEGER PRIMARY KEY,
                started_at INTEGER NOT NULL,
                heartbeat_at INTEGER NOT NULL,
                finished_at INTEGER,
                status TEXT NOT NULL,
                command TEXT NOT NULL,
                error TEXT
            );
        ",
        )
        .expect("create dashboard schema");
    }

    fn insert_issue(conn: &Connection, iid: i64, state: &str, updated_at: i64) {
        conn.execute(
            "INSERT INTO issues (gitlab_id, project_id, iid, title, state, created_at, updated_at, last_seen_at)
             VALUES (?1, 1, ?2, ?3, ?4, ?5, ?5, ?5)",
            rusqlite::params![iid * 100, iid, format!("Issue {iid}"), state, updated_at],
        )
        .expect("insert issue");
    }

    fn insert_mr(conn: &Connection, iid: i64, state: &str, updated_at: i64) {
        conn.execute(
            "INSERT INTO merge_requests (gitlab_id, project_id, iid, title, state, created_at, updated_at, last_seen_at)
             VALUES (?1, 1, ?2, ?3, ?4, ?5, ?5, ?5)",
            rusqlite::params![iid * 100 + 50, iid, format!("MR {iid}"), state, updated_at],
        )
        .expect("insert mr");
    }

    /// TDD anchor test from bead spec.
    #[test]
    fn test_schema_preflight_rejects_old() {
        let conn = Connection::open_in_memory().unwrap();
        conn.execute_batch(
            "CREATE TABLE schema_version (version INTEGER);
             INSERT INTO schema_version (version) VALUES (1);",
        )
        .unwrap();

        let result = check_schema_version(&conn, 20);
        assert!(matches!(
            result,
            SchemaCheck::Incompatible {
                found: 1,
                minimum: 20
            }
        ));
    }

    #[test]
    fn test_schema_preflight_accepts_compatible() {
        let conn = Connection::open_in_memory().unwrap();
        conn.execute_batch(
            "CREATE TABLE schema_version (version INTEGER);
             INSERT INTO schema_version (version) VALUES (26);",
        )
        .unwrap();

        let result = check_schema_version(&conn, 20);
        assert!(matches!(result, SchemaCheck::Compatible { version: 26 }));
    }

    #[test]
    fn test_schema_preflight_exact_minimum() {
        let conn = Connection::open_in_memory().unwrap();
        conn.execute_batch(
            "CREATE TABLE schema_version (version INTEGER);
             INSERT INTO schema_version (version) VALUES (20);",
        )
        .unwrap();

        let result = check_schema_version(&conn, 20);
        assert!(matches!(result, SchemaCheck::Compatible { version: 20 }));
    }

    #[test]
    fn test_schema_preflight_no_db() {
        let conn = Connection::open_in_memory().unwrap();
        let result = check_schema_version(&conn, 20);
        assert!(matches!(result, SchemaCheck::NoDB));
    }

    #[test]
    fn test_schema_preflight_empty_schema_version_table() {
        let conn = Connection::open_in_memory().unwrap();
        conn.execute_batch("CREATE TABLE schema_version (version INTEGER);")
            .unwrap();

        let result = check_schema_version(&conn, 20);
        assert!(matches!(result, SchemaCheck::NoDB));
    }

    #[test]
    fn test_check_data_readiness_empty() {
        let conn = Connection::open_in_memory().unwrap();
        create_dashboard_schema(&conn);
        conn.execute_batch(
            "CREATE TABLE schema_version (version INTEGER);
             INSERT INTO schema_version (version) VALUES (26);",
        )
        .unwrap();

        let readiness = check_data_readiness(&conn).unwrap();
        assert!(!readiness.has_issues);
        assert!(!readiness.has_mrs);
        assert!(!readiness.has_documents);
        assert_eq!(readiness.schema_version, 26);
        assert!(!readiness.has_any_data());
    }

    #[test]
    fn test_check_data_readiness_with_data() {
        let conn = Connection::open_in_memory().unwrap();
        create_dashboard_schema(&conn);
        conn.execute_batch(
            "CREATE TABLE schema_version (version INTEGER);
             INSERT INTO schema_version (version) VALUES (26);",
        )
        .unwrap();

        insert_issue(&conn, 1, "opened", 1_700_000_000_000);
        insert_mr(&conn, 1, "merged", 1_700_000_000_000);

        let readiness = check_data_readiness(&conn).unwrap();
        assert!(readiness.has_issues);
        assert!(readiness.has_mrs);
        assert!(!readiness.has_documents);
        assert_eq!(readiness.schema_version, 26);
        assert!(readiness.has_any_data());
    }

    #[test]
    fn test_check_data_readiness_documents_table_missing() {
        let conn = Connection::open_in_memory().unwrap();
        create_dashboard_schema(&conn);
        // No documents table — should still work.

        let readiness = check_data_readiness(&conn).unwrap();
        assert!(!readiness.has_documents);
    }
}
