//! Integration tests: view → source table/view relationship emission.
use camino::Utf8PathBuf;
use psql_scip::{
    core::{ddl::walk_ddl, schema::SchemaModel},
    emit::emit_index,
    parse::split_and_parse,
};

fn manifest_dir() -> Utf8PathBuf {
    let base =
        std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR must be set by cargo test");
    Utf8PathBuf::from(base)
}

fn build_index_from_sql(sql: &str) -> scip::types::Index {
    let base = manifest_dir();
    let schema_path = base.join("tests/fixtures/schema_view_rels.sql");
    let (stmts, failures) = split_and_parse(sql).expect("sql must parse");
    let mut model = SchemaModel::default();
    walk_ddl(&stmts, &mut model);
    emit_index(&schema_path, sql, &failures, &model, &[], &base)
}

fn find_sym<'a>(
    doc: &'a scip::types::Document,
    needle: &str,
) -> &'a scip::types::SymbolInformation {
    doc.symbols
        .iter()
        .find(|s| s.symbol.contains(needle))
        .unwrap_or_else(|| panic!("must find symbol containing '{needle}'"))
}

#[test]
fn view_has_reference_relationship_to_source_table() {
    let sql = concat!(
        "CREATE TABLE public.users (id bigint NOT NULL, name text);",
        "CREATE VIEW public.active_users AS SELECT id, name FROM public.users WHERE id > 0;",
    );
    let index = build_index_from_sql(sql);
    let doc = &index.documents[0];

    let table_sym = find_sym(doc, "public/users.").symbol.clone();
    let view_si = find_sym(doc, "active_users");

    assert!(
        view_si
            .relationships
            .iter()
            .any(|r| r.symbol == table_sym && r.is_reference),
        "active_users view must have is_reference relationship to users table; got {:?}",
        view_si.relationships,
    );
}

#[test]
fn view_referencing_two_tables_has_two_relationships() {
    let sql = concat!(
        "CREATE TABLE public.users (id bigint NOT NULL);",
        "CREATE TABLE public.orders (id bigint NOT NULL, user_id bigint NOT NULL);",
        "CREATE VIEW public.user_orders AS ",
        "  SELECT u.id, o.id AS order_id FROM public.users u JOIN public.orders o ON u.id = o.user_id;",
    );
    let index = build_index_from_sql(sql);
    let doc = &index.documents[0];

    let users_sym = find_sym(doc, "public/users.").symbol.clone();
    let orders_sym = find_sym(doc, "public/orders.").symbol.clone();
    let view_si = find_sym(doc, "user_orders");

    assert!(
        view_si
            .relationships
            .iter()
            .any(|r| r.symbol == users_sym && r.is_reference),
        "user_orders must reference users; got {:?}",
        view_si.relationships,
    );
    assert!(
        view_si
            .relationships
            .iter()
            .any(|r| r.symbol == orders_sym && r.is_reference),
        "user_orders must reference orders; got {:?}",
        view_si.relationships,
    );
}

#[test]
fn view_referencing_unknown_table_emits_no_relationship() {
    let sql = concat!(
        "CREATE TABLE public.users (id bigint NOT NULL);",
        "CREATE VIEW public.v AS SELECT id FROM public.nonexistent;",
    );
    let index = build_index_from_sql(sql);
    let doc = &index.documents[0];
    let view_si = find_sym(doc, "public/v.");

    assert!(
        view_si.relationships.is_empty(),
        "view referencing unknown table must emit no relationships; got {:?}",
        view_si.relationships,
    );
}

#[test]
fn view_referencing_another_view_has_relationship() {
    let sql = concat!(
        "CREATE TABLE public.users (id bigint NOT NULL);",
        "CREATE VIEW public.base_view AS SELECT id FROM public.users;",
        "CREATE VIEW public.derived_view AS SELECT id FROM public.base_view;",
    );
    let index = build_index_from_sql(sql);
    let doc = &index.documents[0];

    let base_sym = find_sym(doc, "base_view").symbol.clone();
    let derived_si = find_sym(doc, "derived_view");

    assert!(
        derived_si
            .relationships
            .iter()
            .any(|r| r.symbol == base_sym && r.is_reference),
        "derived_view must reference base_view; got {:?}",
        derived_si.relationships,
    );
}
