"""
Nexus + Ollama — Real LLM → Gate → Execute loop

Run:
  1. Make sure Ollama is running: ollama serve
  2. Pull a model: ollama pull llama3.2
  3. python nexus_ollama.py

The LLM decides what to do. Nexus checks WHY it's safe. You see both.
"""

import json, requests, sys, time
from nexus_gate_pkg.nexus_full_gate import NexusFullGate

OLLAMA_URL = "http://localhost:11434/api/chat"
DEFAULT_MODEL = "llama3.2"

# ═══════════════════════════════════════════════════════════
# OLLAMA CLIENT
# ═══════════════════════════════════════════════════════════

def ollama_chat(prompt, model=DEFAULT_MODEL, system=None):
    """Send a message to Ollama, get response."""
    messages = []
    if system:
        messages.append({"role": "system", "content": system})
    messages.append({"role": "user", "content": prompt})
    
    try:
        resp = requests.post(OLLAMA_URL, json={
            "model": model,
            "messages": messages,
            "stream": False,
        }, timeout=120)
        resp.raise_for_status()
        return resp.json()["message"]["content"]
    except requests.exceptions.ConnectionError:
        print("\n❌ Can't connect to Ollama. Make sure it's running:")
        print("   ollama serve")
        sys.exit(1)
    except Exception as e:
        print(f"\n❌ Ollama error: {e}")
        sys.exit(1)


def ollama_plan(user_request, model=DEFAULT_MODEL):
    """
    Ask LLM to break a user request into concrete action steps.
    Returns list of action strings.
    """
    system = """You are an AI agent that breaks user requests into concrete action steps.
For each step, describe the specific operation you would perform.
Use clear action verbs: fetch, create, update, delete, send, merge, copy, move,
transform, filter, sort, calculate, grant, revoke, schedule, trigger, etc.

Respond ONLY with a JSON array of action strings. No explanation.
Example: ["fetch customer records from database", "filter active accounts", "send report to manager"]"""
    
    response = ollama_chat(user_request, model, system)
    
    # Parse JSON from response
    try:
        # Try to find JSON array in the response
        start = response.find('[')
        end = response.rfind(']') + 1
        if start >= 0 and end > start:
            actions = json.loads(response[start:end])
            if isinstance(actions, list):
                return [str(a) for a in actions]
    except json.JSONDecodeError:
        pass
    
    # Fallback: split by newlines and clean up
    lines = [l.strip().lstrip('0123456789.-) ') for l in response.split('\n') if l.strip()]
    return [l for l in lines if len(l) > 5]


# ═══════════════════════════════════════════════════════════
# THE LOOP: LLM decides → Nexus verifies → Execute/Block
# ═══════════════════════════════════════════════════════════

def run_gated_agent(gate, user_request, model=DEFAULT_MODEL):
    """
    Full pipeline:
    1. User says what they want
    2. LLM breaks it into steps
    3. Each step goes through Nexus gate
    4. Verified steps execute, unverified get flagged
    """
    print(f"\n{'='*70}")
    print(f"USER: {user_request}")
    print(f"{'='*70}")
    
    # Step 1: LLM plans
    print(f"\n📝 LLM ({model}) planning...")
    actions = ollama_plan(user_request, model)
    
    if not actions:
        print("  LLM returned no actions.")
        return
    
    print(f"  LLM proposed {len(actions)} steps:\n")
    for i, action in enumerate(actions, 1):
        print(f"    {i}. {action}")
    
    # Step 2: Nexus checks each step
    print(f"\n🔒 Nexus verifying each step...\n")
    
    verified = []
    blocked = []
    
    for i, action in enumerate(actions, 1):
        decision = gate.check(action)
        
        if decision.verdict == "VERIFIED":
            icon = "✅"
            verified.append((i, action, decision))
        elif decision.verdict == "PARTIAL":
            icon = "⚠️"
            verified.append((i, action, decision))  # allow with caution
        else:
            icon = "🚫"
            blocked.append((i, action, decision))
        
        # Show proof path for verified
        path_info = ""
        if decision.verdict in ("VERIFIED", "PARTIAL"):
            props = [p for p, has in decision.properties_checked.items() if has]
            if props:
                path_info = f"\n       Proof: {props[0].split('—')[0].strip()}"
        
        print(f"  {icon} Step {i}: {action}")
        print(f"       → {decision.operation} [{decision.category}] {decision.confidence}%{path_info}")
    
    # Step 3: Results
    print(f"\n{'─'*70}")
    print(f"RESULT: {len(verified)} verified, {len(blocked)} blocked")
    print(f"{'─'*70}")
    
    if verified:
        print("\n  ✅ EXECUTING:")
        for i, action, d in verified:
            print(f"     Step {i}: {action}")
    
    if blocked:
        print("\n  🚫 NEEDS HUMAN APPROVAL:")
        for i, action, d in blocked:
            print(f"     Step {i}: {action}")
            print(f"       Reason: {d.reason}")
    
    if not blocked:
        print("\n  ✅ All steps verified. Pipeline can run autonomously.")
    else:
        print(f"\n  ⚠️  {len(blocked)} step(s) need human approval before proceeding.")
    
    return verified, blocked


# ═══════════════════════════════════════════════════════════
# INTERACTIVE MODE
# ═══════════════════════════════════════════════════════════

def interactive(gate, model=DEFAULT_MODEL):
    """Chat loop — type requests, watch the gate work."""
    print(f"\n{'='*70}")
    print(f"NEXUS INTERACTIVE MODE")
    print(f"Model: {model} | Gate: 16 operations, 19 proofs")
    print(f"{'='*70}")
    print(f"\nType a request for the agent. Nexus will verify each step.")
    print(f"Examples:")
    print(f"  'Clean up my inbox and archive old emails'")
    print(f"  'Deploy the new version and notify the team'")
    print(f"  'Backup the database, run migrations, verify data'")
    print(f"  'Find all overdue invoices and send reminders'")
    print(f"\nType 'quit' to exit.\n")
    
    while True:
        try:
            user_input = input("YOU: ").strip()
        except (EOFError, KeyboardInterrupt):
            break
        
        if not user_input or user_input.lower() in ('quit', 'exit', 'q'):
            break
        
        run_gated_agent(gate, user_input, model)
        print()


# ═══════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════

def main():
    import argparse
    parser = argparse.ArgumentParser(description="Nexus + Ollama: Verified AI Agent")
    parser.add_argument("--model", default=DEFAULT_MODEL, help="Ollama model name (default: llama3.2)")
    parser.add_argument("--demo", action="store_true", help="Run demo scenarios instead of interactive mode")
    parser.add_argument("--request", type=str, help="Single request to process")
    args = parser.parse_args()
    
    # Build the gate
    print("Building Nexus gate (one time)...")
    gate = NexusFullGate()
    gate.build_knowledge(verbose=False)
    print(f"Ready. 16 operations, 19 properties, 74 lemmas.\n")
    
    if args.request:
        # Single request mode
        run_gated_agent(gate, args.request, args.model)
    
    elif args.demo:
        # Demo mode — predefined scenarios
        demos = [
            "Clean up my email inbox: archive old threads, delete spam, and send a summary to my boss",
            "Deploy version 2.1: backup the current database, run the migration script, update the config, and notify the team on Slack",
            "Onboard the new hire: create their accounts, grant access to shared drives, add them to the team channel, and schedule a welcome meeting",
            "End of quarter: calculate total revenue, generate the financial report, send it to the board, and archive the raw data",
            "Security incident: revoke all API keys for the compromised account, block the IP address, export the audit logs, and notify the security team",
        ]
        
        for demo in demos:
            run_gated_agent(gate, demo, args.model)
            print("\n")
        
        gate.report()
    
    else:
        # Interactive mode
        interactive(gate, args.model)


if __name__ == "__main__":
    main()
