use camino::Utf8Path;
use pg_query::NodeEnum;
use psql_scip::parse::{parse_file, split_and_parse};

fn fixture(name: &str) -> camino::Utf8PathBuf {
    // CARGO_MANIFEST_DIR points at crates/psql-scip at test time.
    let base =
        std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set by cargo test");
    camino::Utf8PathBuf::from(base)
        .join("tests/fixtures")
        .join(name)
}

#[test]
fn simple_select_yields_one_statement() {
    let path = fixture("simple_select.sql");
    let (stmts, _) = parse_file(&path).expect("simple_select.sql must parse");
    assert_eq!(stmts.len(), 1, "one statement expected");
}

#[test]
fn simple_select_root_is_select_stmt() {
    let path = fixture("simple_select.sql");
    let (stmts, _) = parse_file(&path).expect("simple_select.sql must parse");
    assert!(
        matches!(stmts[0].root, NodeEnum::SelectStmt(_)),
        "root must be SelectStmt, got something else"
    );
}

#[test]
fn create_table_yields_one_statement() {
    let path = fixture("create_table_with_columns.sql");
    let (stmts, _) = parse_file(&path).expect("create_table_with_columns.sql must parse");
    assert_eq!(stmts.len(), 1, "one CREATE TABLE expected");
}

#[test]
fn create_table_root_is_create_stmt() {
    let path = fixture("create_table_with_columns.sql");
    let (stmts, _) = parse_file(&path).expect("create_table_with_columns.sql must parse");
    assert!(
        matches!(stmts[0].root, NodeEnum::CreateStmt(_)),
        "root must be CreateStmt"
    );
}

#[test]
fn create_function_yields_one_statement() {
    let path = fixture("create_function.sql");
    let (stmts, _) = parse_file(&path).expect("create_function.sql must parse");
    assert_eq!(stmts.len(), 1, "one CREATE FUNCTION expected");
}

#[test]
fn create_function_root_is_create_function_stmt() {
    let path = fixture("create_function.sql");
    let (stmts, _) = parse_file(&path).expect("create_function.sql must parse");
    assert!(
        matches!(stmts[0].root, NodeEnum::CreateFunctionStmt(_)),
        "root must be CreateFunctionStmt"
    );
}

#[test]
fn cte_with_alias_yields_one_statement() {
    let path = fixture("cte_with_alias.sql");
    let (stmts, _) = parse_file(&path).expect("cte_with_alias.sql must parse");
    assert_eq!(stmts.len(), 1, "one SELECT with CTE expected");
}

#[test]
fn cte_with_alias_root_is_select_stmt() {
    let path = fixture("cte_with_alias.sql");
    let (stmts, _) = parse_file(&path).expect("cte_with_alias.sql must parse");
    assert!(
        matches!(stmts[0].root, NodeEnum::SelectStmt(_)),
        "CTE root must be SelectStmt"
    );
}

#[test]
fn missing_file_returns_io_error() {
    let path = Utf8Path::new("/nonexistent/path/query.sql");
    let result = parse_file(path);
    assert!(
        matches!(result, Err(psql_scip::errors::ParseError::Io { .. })),
        "missing file must return ParseError::Io"
    );
}

#[test]
fn empty_file_returns_empty_input_error() {
    let dir = tempfile::tempdir().expect("tempdir must be created");
    let path_std = dir.path().join("empty.sql");
    std::fs::write(&path_std, "   \n").expect("write must succeed");
    // Convert to Utf8Path — temp paths are always UTF-8 on our platform.
    let path_str = path_std.to_str().expect("temp path must be UTF-8");
    let path = Utf8Path::new(path_str);

    let result = parse_file(path);
    assert!(
        matches!(result, Err(psql_scip::errors::ParseError::EmptyInput)),
        "whitespace-only file must return EmptyInput"
    );
}

#[test]
fn split_and_parse_exposes_node_enum() {
    let (stmts, _) = split_and_parse("SELECT 42;").expect("must parse");
    assert_eq!(stmts.len(), 1);
    assert!(
        matches!(stmts[0].root, NodeEnum::SelectStmt(_)),
        "root must be SelectStmt"
    );
}
