"""
Phase 4D: Analogical Reasoning

Extracts 2-hop subgraphs and applies Structure Mapping Engine (SME)
to find isomorphic topological structures across domains.
"""
from typing import Dict, Any, List
import networkx as nx
from networkx.algorithms import isomorphism
from pulse.graph.counterfactual import build_shadow_graph
from pulse.core.brain_db import get_conn
from pulse.core import event_bus

def build_analogy(node_a: str, node_b: str, radius: int = 2) -> Dict[str, Any]:
    """
    Checks for structural analogy between two nodes.
    Returns analogy score and mapping if successful.
    """
    G_a = build_shadow_graph(node_a, radius=radius)
    G_b = build_shadow_graph(node_b, radius=radius)
    
    if not G_a.nodes or not G_b.nodes:
        return {"analogy_score": 0.0, "match": False}

    # Size guard: skip isomorphism on large graphs (expensive)
    if len(G_a.nodes) > 100 or len(G_b.nodes) > 100:
        return {"analogy_score": 0.0, "match": False}

    # We only care about edge directions and types (CAUSES/PREVENTS mapping to weight sign)
    # We don't require exact node labels to match, just graph topology
    def edge_match(e1, e2):
        # Edges must have the same causal direction (both positive or both negative)
        w1_sign = 1 if e1.get("weight", 0) > 0 else -1
        w2_sign = 1 if e2.get("weight", 0) > 0 else -1
        return w1_sign == w2_sign

    matcher = isomorphism.DiGraphMatcher(G_a, G_b, edge_match=edge_match)
    
    is_isomorphic = matcher.subgraph_is_isomorphic()
    if not is_isomorphic:
        return {"analogy_score": 0.0, "match": False}
        
    mapping = next(matcher.subgraph_isomorphisms_iter())
    
    # Check cross-domain score boost
    conn = get_conn()
    with conn:
        res = conn.execute("SELECT domain FROM graph_nodes WHERE id IN (?, ?)", (node_a, node_b)).fetchall()
        domains = [r["domain"] for r in res if "domain" in r.keys()]
        
    cross_domain = len(set(domains)) > 1
    score = 0.6
    if cross_domain:
        score = 0.8 # Boost for structural matches across different domains

    # Add ANALOGOUS_TO edge
    try:
        with conn:
            conn.execute("""
                INSERT INTO graph_edges (from_node, to_node, edge_type, confidence)
                VALUES (?, ?, 'ANALOGOUS_TO', ?)
                ON CONFLICT(from_node, edge_type, to_node) DO NOTHING
            """, (node_a, node_b, score))
            conn.execute("""
                INSERT INTO graph_edges (from_node, to_node, edge_type, confidence)
                VALUES (?, ?, 'ANALOGOUS_TO', ?)
                ON CONFLICT(from_node, edge_type, to_node) DO NOTHING
            """, (node_b, node_a, score))
    except Exception as e:
        event_bus.publish("analogy.edge_error", "analogy", {"node_a": node_a, "node_b": node_b, "error": str(e)})
        
    return {
        "analogy_score": score,
        "match": True,
        "cross_domain": cross_domain,
        "mapping": mapping
    }
