use pg_query::NodeEnum;

use crate::{
    core::{resolver::ResolvedRef, schema::SchemaModel},
    parse::split_and_parse,
};

use super::{emit::emit_stmt, scope::ScopeStack};

/// Walk the SQL body of a `CREATE FUNCTION` and emit any references found.
///
/// The body is an isolated scope — no outer aliases or CTEs apply.
/// Silently skips if no body is found or if it fails to parse.
pub(super) fn emit_function_body_refs(
    stmt: &pg_query::protobuf::CreateFunctionStmt,
    model: &SchemaModel,
    out: &mut Vec<ResolvedRef>,
) {
    let Some(body_sql) = extract_body_sql(stmt) else {
        return;
    };
    let Ok((body_stmts, _)) = split_and_parse(body_sql) else {
        tracing::debug!("function body SQL did not parse, skipping body refs");
        return;
    };
    for body_stmt in &body_stmts {
        let scope = ScopeStack::default();
        emit_stmt(body_stmt, &scope, model, out);
    }
}

fn extract_body_sql(stmt: &pg_query::protobuf::CreateFunctionStmt) -> Option<&str> {
    for opt_node in &stmt.options {
        let Some(NodeEnum::DefElem(de)) = opt_node.node.as_ref() else {
            continue;
        };
        if de.defname != "as" {
            continue;
        }
        let inner = de.arg.as_ref()?.node.as_ref()?;
        if let NodeEnum::List(list) = inner {
            for item in &list.items {
                if let Some(NodeEnum::String(s)) = item.node.as_ref() {
                    if !s.sval.is_empty() {
                        return Some(s.sval.as_str());
                    }
                }
            }
        }
        if let NodeEnum::String(s) = inner {
            if !s.sval.is_empty() {
                return Some(s.sval.as_str());
            }
        }
    }
    None
}
