//! Python call-site detection and post-pass call resolver.
//!
//! `emit_calls` — walk a function body recording raw call targets.
//! `resolve_calls` — post-pass that rewrites raw targets into typed Calls/Awaits edges.

use ckg_core::{Edge, EdgeKind};
use tree_sitter::Node;

use crate::util::text;
use super::imports::{
    collect_python_param_types, collect_python_annotated_types,
};
use super::Ctx;

pub(crate) fn emit_calls(ctx: &mut Ctx<'_>, node: Node<'_>, src: &str, src_id: &str) {
    // var_name → annotated type, harvested from `def f(x: T)` params and
    // `x: T = …` annotated assignments inside the function body.
    let mut types: std::collections::HashMap<String, String> = std::collections::HashMap::new();
    if let Some(params) = node
        .parent()
        .and_then(|f| f.child_by_field_name("parameters"))
    {
        collect_python_param_types(params, src, &mut types);
    }
    collect_python_annotated_types(node, src, &mut types);

    let mut stack = vec![node];
    while let Some(n) = stack.pop() {
        if n.kind() == "call"
            && let Some(func) = n.child_by_field_name("function")
        {
            let target = text(func, src).to_string();
            let resolved = if let Some(dot) = target.find('.')
                && let head = &target[..dot]
                && !head.contains('.')
                && let Some(ty) = types.get(head)
            {
                Some(format!("{ty}{}", &target[dot..]))
            } else {
                None
            };
            let is_awaited = is_under_await(n, node);
            ctx.raw_calls
                .push((src_id.to_string(), resolved.unwrap_or(target), is_awaited));
        }
        let mut c = n.walk();
        for child in n.children(&mut c) {
            stack.push(child);
        }
    }
}

/// True if any ancestor of `n` (within the function body) is an `await`.
fn is_under_await(n: Node<'_>, body_root: Node<'_>) -> bool {
    let mut cur = n;
    while cur != body_root {
        let Some(parent) = cur.parent() else { break };
        if parent.kind() == "await" {
            return true;
        }
        cur = parent;
    }
    false
}

/// Confidence tiers:
///   1.0 bare-name leaf matches a file-local symbol
///   0.9 import alias resolution
///   0.7 `Type.method` — receiver type known via annotation
///   0.5 unresolved bare name
///   0.4 `self.x.method` — receiver type unknown
pub(crate) fn resolve_calls(ctx: &mut Ctx<'_>) {
    let raw = std::mem::take(&mut ctx.raw_calls);
    for (src, raw_target, is_awaited) in raw {
        let head = raw_target
            .split('.')
            .next()
            .unwrap_or(&raw_target)
            .to_string();
        let is_dotted = raw_target.contains('.');
        let is_self_method = head == "self" && is_dotted;
        // Type-aware dispatch: head is uppercase identifier (looks like a class)
        // and we have a multi-segment path ending in `.method`.
        let head_is_class = head
            .chars()
            .next()
            .map(|c| c.is_ascii_uppercase())
            .unwrap_or(false);

        let (dst, confidence) = if !is_dotted && let Some(id) = ctx.name_to_id.get(&raw_target) {
            (id.clone(), 1.0)
        } else if let Some(full) = ctx.aliases.get(&head) {
            let suffix = if raw_target.len() > head.len() {
                &raw_target[head.len()..]
            } else {
                ""
            };
            (format!("{full}{suffix}"), 0.9)
        } else if is_dotted && head_is_class {
            (raw_target, 0.7)
        } else if is_self_method {
            let leaf = raw_target
                .rsplit('.')
                .next()
                .unwrap_or(&raw_target)
                .to_string();
            (leaf, 0.4)
        } else {
            (raw_target, 0.5)
        };
        ctx.out.edges.push(Edge {
            kind: EdgeKind::Calls,
            src: src.clone(),
            dst: dst.clone(),
            confidence,
        });
        if is_awaited {
            ctx.out.edges.push(Edge {
                kind: EdgeKind::Awaits,
                src,
                dst,
                confidence,
            });
        }
    }
}

