#!/usr/bin/env python3
"""
The Thalamus — Signal Contract, Routing & Arbitration
Every signal passes through here. Enforces the formal schema.
Applies conflict resolution when structures disagree.
"""
import json
import re
from datetime import datetime
from pathlib import Path

from brain.state import (
    new_signal, load_state, save_state, enqueue_signal,
    log_absence, log_presence,
    ABSENCE_CATEGORIES
)

# Import brain structures
import sys
sys.path.insert(0, str(Path(__file__).parent.parent))
from brain import amygdala, hippocampus

# Routing order (priority 0 = first)
ROUTING_ORDER = ["medulla", "rcomplex", "amygdala", "hippocampus", "cortex", "corpus", "akashic", "lethe"]

# =============================================================================
# Cortex-lite (no LLM — pure keyword/regex analysis)
# =============================================================================

CONTENT_MAP = {
    "question": ["how", "why", "what", "when", "where", "who", "explain", "?"],
    "command": ["create", "make", "build", "run", "start", "stop", "delete", "send"],
    "information": ["find", "search", "lookup", "check", "status", "show", "list"],
    "social": ["hello", "hi", "hey", "thanks", "thank", "please", "bye", "sorry"],
    "creative": ["write", "story", "poem", "song", "design", "imagine", "draw"],
}

TOPIC_KEYWORDS = {
    "memory": ["memory", "recall", "remember", "forget", "encode", "store"],
    "system": ["system", "process", "signal", "brain", "medulla", "cortex"],
    "file": ["file", "read", "write", "edit", "create", "delete"],
    "network": ["network", "connect", "sync", "send", "post", "publish"],
    "time": ["time", "schedule", "when", "cron", "periodic", "daily"],
    "self": ["self", "aware", "conscious", "alive", "exist", "I am"],
}

def analyze_intent(text):
    text = text.lower()
    for intent, keywords in CONTENT_MAP.items():
        if any(kw in text for kw in keywords):
            return intent
    return "unknown"

def extract_topics(text):
    text = text.lower()
    found = []
    for topic, keywords in TOPIC_KEYWORDS.items():
        if any(kw in text for kw in keywords):
            found.append(topic)
    return found

def extract_entities(text):
    """Extract file paths, URLs, quoted strings."""
    entities = []
    entities.extend(re.findall(r'/[\w/.-]+', text))
    entities.extend(re.findall(r'https?://\S+', text))
    entities.extend(re.findall(r'"([^"]+)"', text))
    return list(set(entities))

def extract_questions(text):
    return re.findall(r'[^.!?]*\?', text)[:3]

def assess_complexity(text):
    words = len(text.split())
    sentences = max(len(re.split(r'[.!?]+', text)), 1)
    avg_words_per_sent = words / sentences
    if words > 80 or avg_words_per_sent > 20:
        return "complex"
    elif words > 30 or avg_words_per_sent > 12:
        return "moderate"
    return "simple"

def predict_actions(text, intent):
    actions = []
    text = text.lower()
    if intent == "command":
        if any(k in text for k in ["create", "make", "build"]):
            actions.append("create_file")
        if any(k in text for k in ["run", "start", "execute"]):
            actions.append("execute")
        if any(k in text for k in ["stop", "kill", "end"]):
            actions.append("terminate")
        if any(k in text for k in ["delete", "remove", "clear"]):
            actions.append("delete")
        if any(k in text for k in ["search", "find", "lookup"]):
            actions.append("search")
        if any(k in text for k in ["send", "post", "publish"]):
            actions.append("publish")
    return actions

def estimate_uncertainty(text, intent):
    """Higher uncertainty when system is asked about things it can't verify."""
    low_certainty = ["maybe", "perhaps", "might", "could be", "not sure", "don't know", "uncertain"]
    text_lower = text.lower()
    if any(phrase in text_lower for phrase in low_certainty):
        return 0.7
    if intent == "question":
        return 0.4
    return 0.2

# =============================================================================
# Conflict Resolution
# =============================================================================

def resolve_conflict(signal):
    """
    Thalamus arbitration when structures disagree.

    Rules (checked in order):
    1. Amygdala Override: salience >= 0.8 → emotional urgency gates response
    2. Hippocampus Override: match_confidence >= 0.9 → prior memory dominates
    3. Arousal Suppression: amygdala.arousal == "overload" → cortex suppressed
    4. Cortex Dominance: salience < 0.5 AND uncertainty < 0.3 → normal analysis
    5. High Uncertainty: uncertainty > 0.8 → default conservatism
    6. Default: systematic conservatism, bias toward inaction on ambiguous signals

    Returns: dict describing how conflict was resolved
    """
    amygdala_block = signal.get("amygdala", {})
    hippocampus_block = signal.get("hippocampus", {})
    cortex_block = signal.get("cortex", {})

    salience = amygdala_block.get("salience", 0.0)
    match_conf = hippocampus_block.get("match_confidence", 0.0)
    uncertainty = cortex_block.get("uncertainty", 0.5)
    arousal = amygdala_block.get("arousal", "calm")

    resolution = {
        "rule": None,
        "winner": None,
        "reason": None
    }

    # Rule 1: Amygdala override (high emotional salience)
    if salience >= 0.8:
        resolution["rule"] = "amygdala_override"
        resolution["winner"] = "amygdala"
        resolution["reason"] = f"Salience {salience} >= 0.8 — emotional urgency gates response"
        signal["final_output"]["conflict_resolved"] = resolution
        return resolution

    # Rule 2: Hippocampus override (strong memory match)
    if match_conf >= 0.9:
        resolution["rule"] = "hippocampus_override"
        resolution["winner"] = "hippocampus"
        resolution["reason"] = f"Match confidence {match_conf} >= 0.9 — prior memory dominates"
        signal["final_output"]["conflict_resolved"] = resolution
        return resolution

    # Rule 3: Arousal overload — suppress cortex
    if arousal == "overload":
        resolution["rule"] = "arousal_suppression"
        resolution["winner"] = "amygdala"
        resolution["reason"] = f"Arousal {arousal} — cortex processing suppressed"
        signal["final_output"]["conflict_resolved"] = resolution
        return resolution

    # Rule 4: Cortex dominance (low salience, low uncertainty)
    if salience < 0.5 and uncertainty < 0.3:
        resolution["rule"] = "cortex_dominance"
        resolution["winner"] = "cortex"
        resolution["reason"] = f"Salience {salience} < 0.5 and uncertainty {uncertainty} < 0.3 — normal analysis"
        signal["final_output"]["conflict_resolved"] = resolution
        return resolution

    # Rule 5: High uncertainty — default conservatism
    if uncertainty > 0.8:
        resolution["rule"] = "high_uncertainty"
        resolution["winner"] = "none"
        resolution["reason"] = f"Uncertainty {uncertainty} > 0.8 — no action on ambiguous signal"
        signal["final_output"]["conflict_resolved"] = resolution
        return resolution

    # Default: systematic conservatism
    resolution["rule"] = "default_conservatism"
    resolution["winner"] = "none"
    resolution["reason"] = f"Ambiguous signal (salience={salience}, uncertainty={uncertainty}) — no action taken"
    signal["final_output"]["conflict_resolved"] = resolution
    return resolution

# =============================================================================
# Main Signal Processor
# =============================================================================

def process_signal(raw_input, source="user", priority="normal"):
    """
    Main entry point: route a raw input through the full brain.
    Enforces the signal contract.
    """
    state = load_state()
    if not state.get("active", False):
        return {"error": "Medulla not active. Run: ghoju-v3 stimulate"}

    # Create signal with formal contract
    signal = new_signal(raw_input, source, priority)

    # Medulla: record autonomic state at receive time
    signal["medulla"]["heartbeat_at_receive"] = state.get("current_beat", 0)
    signal["medulla"]["respiratory_phase"] = state.get("respiratory_phase", "exhale")
    signal["medulla"]["autonomic_load"] = 0.5 if state.get("torpor") else 1.0

    # rcomplex: basic territory status
    signal["rcomplex"]["territory_status"] = state.get("territory_status", "safe")

    # === STRUCTURE PROCESSING IN ORDER ===
    # (No LLM — each module processes its section of the signal contract)

    # Amygdala: emotional tagging + salience
    signal = amygdala.process(signal)

    # Hippocampus: memory encoding + retrieval
    signal = hippocampus.process(signal)

    # Cortex: intent, topics, complexity, questions, predicted actions
    raw = signal["raw_input"]
    cortex_block = signal["cortex"]
    cortex_block["intent"] = analyze_intent(raw)
    cortex_block["entities"] = extract_entities(raw)
    cortex_block["topics"] = extract_topics(raw)
    cortex_block["complexity"] = assess_complexity(raw)
    cortex_block["questions"] = extract_questions(raw)
    cortex_block["predicted_actions"] = predict_actions(raw, cortex_block["intent"])
    cortex_block["uncertainty"] = estimate_uncertainty(raw, cortex_block["intent"])

    # Conflict resolution
    conflict_result = resolve_conflict(signal)
    signal["final_output"]["conflict_resolved"] = conflict_result

    # Akashic: record presence
    log_presence(signal["signal_id"], raw, quality=signal["amygdala"]["valence"])
    signal["akashic"]["recorded"] = True
    signal["akashic"]["quality"] = signal["amygdala"]["valence"]

    # Lethe: check for absence categories (after processing)
    # For now: check if any expected categories appeared in this signal
    # (Future: cross-reference with predicted signals)
    for cat in ABSENCE_CATEGORIES:
        if cat.replace("_", " ") in raw.lower():
            signal["lethe"]["absence_categories"].append(cat)

    # Enqueue for persistence
    enqueue_signal(signal)

    # Update state
    state["last_signal_at"] = datetime.now().isoformat()
    save_state(state)

    return signal

def route_batch(signals):
    """Route multiple signals through the brain."""
    results = []
    for sig in signals:
        result = process_signal(sig["raw_input"], sig.get("source", "user"), sig.get("priority", "normal"))
        results.append(result)
    return results

# =============================================================================
# CLI test
# =============================================================================

if __name__ == "__main__":
    import sys
    if len(sys.argv) > 1:
        raw = " ".join(sys.argv[1:])
        result = process_signal(raw)
        print(json.dumps(result, indent=2))
    else:
        print("Thalamus router — use ghoju-v3 signal to inject signals")
