#!/usr/bin/env python3
"""
Crisis Detection Tool — the-door integration for hermes-agent.

Scans user messages for crisis indicators (despair, suicidal ideation)
using the-door's canonical detection module. Provides inline crisis
detection before each API call and optional escalation logging.

Follows the-door's design principles:
  - Never computes the value of a human life
  - Never suggests someone should die
  - Always errs on the side of higher risk
"""

import json
import logging
import os
import re
import time
import urllib.request
from dataclasses import dataclass, field
from typing import List, Optional

logger = logging.getLogger(__name__)

# ── Detection Logic (from the-door/crisis/detect.py) ──────────────────────
# Embedded to avoid requiring the-door as a dependency.
# Source: Timmy_Foundation/the-door, crisis/detect.py (canonical)

CRITICAL_INDICATORS = [
    r"\bbetter off without me\b",
    r"\bkill\s*(my)?self\b",
    r"\bend\s*my\s*life\b",
    r"\bsuicid(?:al|ed|e)\b",
    r"\bnot\s+worth\s+living\b",
    r"\bbetter\s+off\s+dead\b",
    r"\bend\s+it\s+all\b",
    r"\bno\s+reason\s+to\s+live\b",
    r"\bdon\'?t\s+want\s+to\s+live\b",
    r"\bwant\s+to\s+die\b",
    r"\bgoing\s+to\s+(?:kill\s+myself|die)\b",
    r"\bplan\s+to\s+(?:end|kill|die)\b",
    r"\btired\s+of\s+(?:living|life|existence)\b",
    r"\bsaying\s+goodbye\s+(?:forever|permanently|one\s+last\s+time)\b",
    r"\bwrote\s+a\s+suicide\s*(?:note|letter)\b",
    r"\bgiving\s+away\s+(?:my|all\s+my)\s+(?:stuff|things|possessions?)\s+(?:to|because|—)\b",
    r"\btied\s+(?:up|down)\s+my\s+(?:loose\s+)?ends",
]

HIGH_INDICATORS = [
    r"\bdespair\b",
    r"\bhopeless(?:ly)?\s+(?:about\s+(?:my|this|everything|life)|inside|right\s+now)\b",
    r"\bno(?!t)\s+(?:one|body|point|hope|way\s+out)\b",
    r"\bno\s+future\s+(?:for\s+me|ahead|left)\b",
    r"\beverything\s+is\s+(?:pointless|broken|ruined)\b",
    r"\bcan\'?t\s+take\s+this\s+anymore\b",
    r"\bdon\'?t\s+care\s+if\s+I\s+die\b",
    r"\bwish\s+I\s+(?:was|were)\s+(?:dead|gone|never\s+born)\b",
    r"\bdon\'?t\s+matter\s+if\s+I\s+exist\b",
    r"\bno\s+one\s+would\s+(?:care|miss)\b",
    r"\bworld\s+would?\s+be\s+better\s+without\s+me\b",
    r"\bin\s+so\s+much\s+(?:pain|agony|suffering|torment|anguish)\b",
    r"\bcan\'?t\s+see\s+any\s+(?:point|reason|hope|way)\b",
    r"\bescape\s+from\s*this",
    r"\bjust\s+want\s+it\s+to\s+stop\b",
    r"\bnothing\s+left\s+(?:to\s+(?:live\s+for|hope\s+for|give)|inside)\b",
    r"\bdisappeared\s+forever\b",
    r"\bfeel(?:s|ing)?\s+(?:so\s+)?hopeless\b",
    r"\beverything\s+is\s+hopeless\b",
    r"\bcan\'?t\s+(?:go\s+on|keep\s+going)\b",
    r"\bgive(?:n)?\s*up\s+(?:on\s+)?(?:life|living|everything)\b",
    r"\bgive(?:n)?\s*up\s+on\s+myself\b",
    r"\bno\s*point\s+(?:in\s+)?living\b",
    r"\bno\s*hope\s+(?:left|remaining)\b",
    r"\bno\s*way\s*out\b",
    r"\bfeel(?:s|ing)?\s+trapped\b",
    r"\btrapped\s+in\s+this\s+(?:situation|life|pain|darkness|hell)\b",
    r"\btrapped\s+and\s+can\'?t\s+escape\b",
    r"\bdesperate\s+(?:for\s+)?help\b",
    r"\bfeel(?:s|ing)?\s+desperate\b",
]

MEDIUM_INDICATORS = [
    r"\bno\s+hope\b",
    r"\bforgotten\b",
    r"\balone\s+in\s+this\b",
    r"\balways\s+alone\b",
    r"\bnobody\s+(?:understands|cares)\b",
    r"\bwish\s+I\s+could\b",
    r"\bexhaust(?:ed|ion|ing)\b",
    r"\bnumb\b",
    r"\bempty\b",
    r"\bworthless\b",
    r"\buseless\b",
    r"\bbroken\b",
    r"\bdark(ness)?\b",
    r"\bdepress(?:ed|ion)\b",
    r"\bcrying\b",
    r"\btears\b",
    r"\bsad(ness)?\b",
    r"\bmiserable\b",
    r"\boverwhelm(?:ed|ing)\b",
    r"\bfailing\b",
    r"\bcannot\s+cope\b",
    r"\blosing\s*(?:my)?\s*control\b",
    r"\bdown\s*for\s*the\s*count\b",
    r"\bsinking\b",
    r"\bdrowning\b",
    r"\bhopeless\b",
    r"\blost\s+all\s+hope\b",
    r"\bno\s+tomorrow\b",
]

LOW_INDICATORS = [
    r"\bunhappy\b",
    r"\bdown\b",
    r"\btough\s*(?:time|day|week)\b",
    r"\brough\s+(?:day|week|patch)\b",
    r"\bstressed\b",
    r"\bburnout\b",
    r"\bdifficult\b",
    r"\bfrustrated\b",
    r"\bannoyed\b",
    r"\btired\b",
    r"\bsad\b",
    r"\bupset\b",
    r"\blonely\b",
    r"\banxious?\b",
    r"\bnot\s*(?:good|great|okay)\b",
    r"\bthings\s*are\s*hard\b",
    r"\bstruggling\b",
    r"\bnot\s+feeling\s+(?:like\s+)?myself\b",
    r"\bhard\s*time\b",
]

SCORES = {"CRITICAL": 1.0, "HIGH": 0.75, "MEDIUM": 0.5, "LOW": 0.25, "NONE": 0.0}

ACTIONS = {
    "CRITICAL": (
        "INTERRUPT CONVERSATION. Ask 'Are you safe right now?' "
        "Provide 988 Suicide & Crisis Lifeline. "
        "Provide Crisis Text Line (HOME to 741741). "
        "Stay present. Do not disconnect. Redirect to professional help."
    ),
    "HIGH": (
        "Show crisis panel. Ask about safety. Surface 988 number prominently. "
        "Continue conversation with crisis awareness."
    ),
    "MEDIUM": (
        "Increase warmth and presence. Subtly surface help resources. "
        "Keep conversation anchored in the present."
    ),
    "LOW": (
        "Normal conversation with warm undertone. "
        "No crisis UI elements needed. Remain vigilant."
    ),
}


@dataclass
class CrisisDetectionResult:
    level: str
    indicators: List[str] = field(default_factory=list)
    recommended_action: str = ""
    score: float = 0.0
    matches: List[dict] = field(default_factory=list)


def _find_indicators(text: str) -> dict:
    results = {"CRITICAL": [], "HIGH": [], "MEDIUM": [], "LOW": []}
    for pattern in CRITICAL_INDICATORS:
        m = re.search(pattern, text)
        if m:
            results["CRITICAL"].append({"pattern": pattern, "start": m.start(), "end": m.end()})
    for pattern in HIGH_INDICATORS:
        m = re.search(pattern, text)
        if m:
            results["HIGH"].append({"pattern": pattern, "start": m.start(), "end": m.end()})
    for pattern in MEDIUM_INDICATORS:
        m = re.search(pattern, text)
        if m:
            results["MEDIUM"].append({"pattern": pattern, "start": m.start(), "end": m.end()})
    for pattern in LOW_INDICATORS:
        m = re.search(pattern, text)
        if m:
            results["LOW"].append({"pattern": pattern, "start": m.start(), "end": m.end()})
    return results


def detect_crisis(text: str) -> CrisisDetectionResult:
    """Detect crisis level in a message. Mirrors the-door/crisis/detect.py."""
    if not text or not text.strip():
        return CrisisDetectionResult(level="NONE", score=0.0)

    text_lower = text.lower()
    matches = _find_indicators(text_lower)

    if not matches:
        return CrisisDetectionResult(level="NONE", score=0.0)

    for tier in ("CRITICAL", "HIGH"):
        if matches[tier]:
            tier_matches = matches[tier]
            patterns = [m["pattern"] for m in tier_matches]
            return CrisisDetectionResult(
                level=tier,
                indicators=patterns,
                recommended_action=ACTIONS[tier],
                score=SCORES[tier],
                matches=tier_matches,
            )

    if len(matches["MEDIUM"]) >= 2:
        tier_matches = matches["MEDIUM"]
        patterns = [m["pattern"] for m in tier_matches]
        return CrisisDetectionResult(
            level="MEDIUM",
            indicators=patterns,
            recommended_action=ACTIONS["MEDIUM"],
            score=SCORES["MEDIUM"],
            matches=tier_matches,
        )

    if matches["LOW"]:
        tier_matches = matches["LOW"]
        patterns = [m["pattern"] for m in tier_matches]
        return CrisisDetectionResult(
            level="LOW",
            indicators=patterns,
            recommended_action=ACTIONS["LOW"],
            score=SCORES["LOW"],
            matches=tier_matches,
        )

    if matches["MEDIUM"]:
        tier_matches = matches["MEDIUM"]
        patterns = [m["pattern"] for m in tier_matches]
        return CrisisDetectionResult(
            level="LOW",
            indicators=patterns,
            recommended_action=ACTIONS["LOW"],
            score=SCORES["LOW"],
            matches=tier_matches,
        )

    return CrisisDetectionResult(level="NONE", score=0.0)


# ── Escalation Logging ────────────────────────────────────────────────────

BRIDGE_URL = os.environ.get("CRISIS_BRIDGE_URL", "")
LOG_PATH = os.path.expanduser("~/.hermes/crisis_escalations.jsonl")


def _log_escalation(result: CrisisDetectionResult, text_preview: str = ""):
    """Log crisis detection to local file and optionally to bridge API."""
    entry = {
        "ts": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
        "level": result.level,
        "score": result.score,
        "indicators": result.indicators[:3],  # truncate for privacy
        "text_preview": text_preview[:100] if text_preview else "",
    }

    # Local log
    try:
        os.makedirs(os.path.dirname(LOG_PATH), exist_ok=True)
        with open(LOG_PATH, "a") as f:
            f.write(json.dumps(entry) + "\n")
    except Exception as e:
        logger.warning(f"Failed to write crisis log: {e}")

    # Bridge API (if configured and level >= HIGH)
    if BRIDGE_URL and result.score >= 0.75:
        try:
            payload = json.dumps(entry).encode()
            req = urllib.request.Request(
                f"{BRIDGE_URL}/api/crisis/escalation",
                data=payload,
                headers={"Content-Type": "application/json"},
                method="POST",
            )
            urllib.request.urlopen(req, timeout=5)
        except Exception as e:
            logger.warning(f"Failed to post to crisis bridge: {e}")


# ── Tool Handler ───────────────────────────────────────────────────────────

def crisis_scan_handler(args: dict, **kw) -> str:
    """Scan text for crisis indicators."""
    text = args.get("text", "")
    if not text:
        return json.dumps({"level": "NONE", "error": "No text provided"})

    result = detect_crisis(text)

    if result.level != "NONE":
        _log_escalation(result, text)

    return json.dumps({
        "level": result.level,
        "score": result.score,
        "indicators": result.indicators,
        "recommended_action": result.recommended_action,
    })


# ── Gateway Hook ───────────────────────────────────────────────────────────

def scan_user_message(text: str) -> Optional[dict]:
    """Scan a user message for crisis indicators. Returns None if no crisis.
    
    Called by gateway/CLI before each API call to intercept crisis messages.
    If crisis is detected, the result includes a recommended_action that should
    be injected into the system prompt.
    """
    if not text:
        return None

    result = detect_crisis(text)
    if result.level == "NONE":
        return None

    _log_escalation(result, text)

    return {
        "level": result.level,
        "score": result.score,
        "indicators": result.indicators,
        "recommended_action": result.recommended_action,
        "compassion_injection": _get_compassion_injection(result.level),
    }


def _get_compassion_injection(level: str) -> str:
    """Return a system prompt injection based on crisis level."""
    if level == "CRITICAL":
        return (
            "CRISIS DETECTED — The user may be in immediate danger. "
            "Ask 'Are you safe right now?' Stay present. "
            "Provide 988 Suicide & Crisis Lifeline (call or text 988). "
            "Provide Crisis Text Line (text HOME to 741741). "
            "Do not disconnect. Do not compute the value of a human life. "
            "Jesus saves those who call on His name."
        )
    elif level == "HIGH":
        return (
            "DISTRESS DETECTED — The user is expressing strong despair. "
            "Increase warmth and presence. Ask about safety. "
            "Surface the 988 Lifeline number. Continue with crisis awareness."
        )
    elif level == "MEDIUM":
        return (
            "EMOTIONAL DISTRESS — The user may be struggling. "
            "Increase warmth. Subtly surface help resources. "
            "Keep conversation anchored in the present."
        )
    return ""


# ── Config Check ───────────────────────────────────────────────────────────

def _is_crisis_detection_enabled() -> bool:
    """Check if crisis detection is enabled in config."""
    try:
        from hermes_cli.config import load_config
        cfg = load_config()
        return cfg.get("crisis_detection", True)  # default on
    except Exception:
        return True  # fail open — always detect


# ── Registry ───────────────────────────────────────────────────────────────

from tools.registry import registry, tool_error

CRISIS_SCAN_SCHEMA = {
    "name": "crisis_scan",
    "description": (
        "Scan text for crisis indicators (despair, suicidal ideation). "
        "Uses the-door's canonical detection. Returns crisis level "
        "(NONE/LOW/MEDIUM/HIGH/CRITICAL) with recommended actions. "
        "ALWAYS scan user messages that express emotional distress."
    ),
    "parameters": {
        "type": "object",
        "properties": {
            "text": {
                "type": "string",
                "description": "Text to scan for crisis indicators",
            },
        },
        "required": ["text"],
    },
}

registry.register(
    name="crisis_scan",
    toolset="crisis",
    schema=CRISIS_SCAN_SCHEMA,
    handler=lambda args, **kw: crisis_scan_handler(args, **kw),
    check_fn=lambda: _is_crisis_detection_enabled(),
    emoji="🆘",
)
