#!/usr/bin/env python3
"""
GENESIS RESEARCH ROUTER
========================
Routes all tasks through appropriate research depth before action.

"All action must be research-led. The depth of research must be
proportional to the importance of the task."

This is Genesis's moat for intelligently applied action.
"""

import re
from dataclasses import dataclass, field
from enum import IntEnum
from typing import List, Dict, Optional, Tuple
from datetime import datetime


class ResearchLevel(IntEnum):
    """Research depth levels."""
    TRIVIAL = 0      # No research - just do it
    LIGHT = 1        # 30-60 second verification
    MODERATE = 2     # 2-5 minute standard research
    DEEP = 3         # 10-30 minute comprehensive research
    ULTRATHINK = 4   # 1+ hour maximum rigor


@dataclass
class ComplexityFactors:
    """Factors that determine research depth."""
    reversibility: int = 0      # 0=instant undo, 3=irreversible
    blast_radius: int = 0       # 0=one line, 3=system-wide
    novelty: int = 0            # 0=done before, 3=unknown unknowns
    confidence: int = 0         # 0=100% certain, 3=guessing
    stakes: int = 0             # 0=zero cost, 3=critical

    @property
    def total_score(self) -> int:
        return (self.reversibility + self.blast_radius +
                self.novelty + self.confidence + self.stakes)

    def to_research_level(self) -> ResearchLevel:
        score = self.total_score
        if score <= 2:
            return ResearchLevel.TRIVIAL
        elif score <= 5:
            return ResearchLevel.LIGHT
        elif score <= 9:
            return ResearchLevel.MODERATE
        elif score <= 12:
            return ResearchLevel.DEEP
        else:
            return ResearchLevel.ULTRATHINK


@dataclass
class ResearchGate:
    """Verification gate for a research level."""
    level: ResearchLevel
    checks: List[str]
    passed: List[bool] = field(default_factory=list)
    notes: List[str] = field(default_factory=list)

    def __post_init__(self):
        if not self.passed:
            self.passed = [False] * len(self.checks)
        if not self.notes:
            self.notes = [""] * len(self.checks)

    @property
    def all_passed(self) -> bool:
        return all(self.passed)

    def mark_passed(self, index: int, note: str = ""):
        self.passed[index] = True
        self.notes[index] = note


@dataclass
class ResearchDecision:
    """Complete research routing decision."""
    task: str
    factors: ComplexityFactors
    level: ResearchLevel
    gate: ResearchGate
    escalation_reasons: List[str] = field(default_factory=list)
    timestamp: str = field(default_factory=lambda: datetime.now().isoformat())


# Research gates for each level
RESEARCH_GATES = {
    ResearchLevel.TRIVIAL: [],

    ResearchLevel.LIGHT: [
        "Verified the change location is correct",
        "Checked for obvious conflicts",
    ],

    ResearchLevel.MODERATE: [
        "Read relevant existing code",
        "Checked documentation/patterns",
        "Verified assumptions with evidence",
        "Considered edge cases",
    ],

    ResearchLevel.DEEP: [
        "Multiple sources consulted (minimum 3)",
        "Cross-referenced claims against primary sources",
        "Devil's advocate analysis performed",
        "Documented uncertainty and assumptions",
        "Pre-mortem: What could go wrong?",
    ],

    ResearchLevel.ULTRATHINK: [
        "Exhaustive research from authoritative sources",
        "Full pre-mortem analysis",
        "Adversarial verification (argued against yourself)",
        "Sleep-on-it protocol considered",
        "Decision rationale documented for future reference",
        "Human confirmation obtained for irreversible actions",
    ],
}


class TaskComplexityDetector:
    """Detects task complexity to determine research depth."""

    # Patterns that trigger auto-escalation
    EXTERNAL_CLAIM_PATTERNS = [
        r'\b(replaces?|kills?|obsoletes?|rip)\b',  # Any replacement claim
        r'\b(official|new|upgrade)\b.*\b(version|replacement)\b',
        r'\b(announces?|claims?|says?)\b',
        r'https?://',  # External URLs need verification
        r'\bcompletely\b',  # Absolute claims need verification
    ]

    FOUNDATIONAL_PATTERNS = [
        r'\b(architect|foundation|core|mandate|protocol)\b',
        r'\b(irreversible|permanent|critical)\b',
        r'\b(security|auth|credential|secret)\b',
        r'\b(database|schema|migration)\b',
        r'\b(all\s+agents?|every\s+agent|system-wide)\b',
    ]

    CLICKBAIT_PATTERNS = [
        r'\b(rip|dead|killed|destroyed|replaced)\b',
        r'\b(game.?changer|revolutionary|breakthrough)\b',
        r'\b(never|always|everything|nothing)\b.*\b(again|before)\b',
        r'!{2,}',  # Multiple exclamation marks
        r'\b(shocking|unbelievable|incredible)\b',
    ]

    TRIVIAL_PATTERNS = [
        r'^fix\s+(typo|spelling|format)',
        r'^(commit|push|pull)\b',
        r'^(list|show|display|print)\b',
        r'^add\s+comment',
    ]

    def __init__(self):
        self.escalation_reasons = []

    def assess(self, task: str) -> Tuple[ComplexityFactors, List[str]]:
        """
        Assess task complexity and return factors + escalation reasons.
        """
        self.escalation_reasons = []
        task_lower = task.lower()

        # Start with base assessment
        factors = ComplexityFactors()

        # Check for trivial tasks first
        if self._matches_patterns(task_lower, self.TRIVIAL_PATTERNS):
            return factors, []

        # Assess each factor
        factors.reversibility = self._assess_reversibility(task_lower)
        factors.blast_radius = self._assess_blast_radius(task_lower)
        factors.novelty = self._assess_novelty(task_lower)
        factors.confidence = self._assess_confidence(task_lower)
        factors.stakes = self._assess_stakes(task_lower)

        # Check auto-escalation triggers
        self._check_escalation_triggers(task_lower, factors)

        return factors, self.escalation_reasons

    def _matches_patterns(self, text: str, patterns: List[str]) -> bool:
        return any(re.search(p, text, re.IGNORECASE) for p in patterns)

    def _assess_reversibility(self, task: str) -> int:
        if any(w in task for w in ['delete', 'remove', 'drop', 'destroy']):
            return 3
        if any(w in task for w in ['migrate', 'rename', 'refactor']):
            return 2
        if any(w in task for w in ['update', 'modify', 'change']):
            return 1
        return 0

    def _assess_blast_radius(self, task: str) -> int:
        if any(w in task for w in ['system', 'all files', 'entire', 'global']):
            return 3
        if any(w in task for w in ['multiple', 'several', 'across']):
            return 2
        if any(w in task for w in ['file', 'module', 'component']):
            return 1
        return 0

    def _assess_novelty(self, task: str) -> int:
        if any(w in task for w in ['new', 'first time', 'never', 'unknown']):
            return 3
        if any(w in task for w in ['different', 'alternative', 'explore']):
            return 2
        if any(w in task for w in ['similar', 'like before', 'standard']):
            return 1
        return 0

    def _assess_confidence(self, task: str) -> int:
        # Lower confidence = higher score
        if any(w in task for w in ['maybe', 'might', 'possibly', 'uncertain']):
            return 3
        if any(w in task for w in ['probably', 'likely', 'should']):
            return 2
        if any(w in task for w in ['assume', 'think', 'believe']):
            return 1
        return 0

    def _assess_stakes(self, task: str) -> int:
        if any(w in task for w in ['security', 'credential', 'production', 'money']):
            return 3
        if any(w in task for w in ['important', 'critical', 'essential']):
            return 2
        if any(w in task for w in ['user', 'client', 'customer']):
            return 1
        return 0

    def _check_escalation_triggers(self, task: str, factors: ComplexityFactors):
        """Check for auto-escalation triggers."""

        # External claims need DEEP research minimum (score >= 10)
        if self._matches_patterns(task, self.EXTERNAL_CLAIM_PATTERNS):
            self.escalation_reasons.append(
                "EXTERNAL_CLAIM: Making claims about external systems requires DEEP verification"
            )
            # Force minimum DEEP level (10+ score)
            factors.novelty = 3
            factors.confidence = 3
            factors.stakes = max(factors.stakes, 2)
            factors.blast_radius = max(factors.blast_radius, 2)

        # Clickbait titles need extra scrutiny - force DEEP minimum
        if self._matches_patterns(task, self.CLICKBAIT_PATTERNS):
            self.escalation_reasons.append(
                "CLICKBAIT_DETECTED: Dramatic claims require DEEP primary source verification"
            )
            factors.confidence = max(factors.confidence, 3)
            factors.novelty = max(factors.novelty, 2)
            factors.stakes = max(factors.stakes, 2)

        # Foundational decisions need ULTRATHINK (score >= 13)
        if self._matches_patterns(task, self.FOUNDATIONAL_PATTERNS):
            self.escalation_reasons.append(
                "FOUNDATIONAL_DECISION: Affects core system, requires ULTRATHINK maximum rigor"
            )
            # Force ULTRATHINK level (13+ score)
            factors.stakes = 3
            factors.reversibility = 3
            factors.blast_radius = max(factors.blast_radius, 3)
            factors.novelty = max(factors.novelty, 2)
            factors.confidence = max(factors.confidence, 2)


class ResearchRouter:
    """
    Routes tasks through appropriate research depth.

    Usage:
        router = ResearchRouter()
        decision = router.route("Implement new authentication system")
        print(f"Research level: {decision.level.name}")
        print(f"Gates to pass: {decision.gate.checks}")
    """

    def __init__(self):
        self.detector = TaskComplexityDetector()
        self.history: List[ResearchDecision] = []

    def route(self, task: str) -> ResearchDecision:
        """
        Route a task to appropriate research depth.

        Returns ResearchDecision with level, gates, and escalation reasons.
        """
        # Assess complexity
        factors, escalation_reasons = self.detector.assess(task)

        # Determine level
        level = factors.to_research_level()

        # Get gate checks for this level
        checks = RESEARCH_GATES.get(level, [])
        gate = ResearchGate(level=level, checks=checks)

        # Create decision
        decision = ResearchDecision(
            task=task,
            factors=factors,
            level=level,
            gate=gate,
            escalation_reasons=escalation_reasons
        )

        self.history.append(decision)
        return decision

    def verify_gate(self, decision: ResearchDecision) -> bool:
        """
        Check if all gate requirements are satisfied.
        """
        if decision.level == ResearchLevel.TRIVIAL:
            return True
        return decision.gate.all_passed

    def get_level_description(self, level: ResearchLevel) -> Dict:
        """Get description and time estimate for a research level."""
        descriptions = {
            ResearchLevel.TRIVIAL: {
                "name": "TRIVIAL",
                "time": "0 seconds",
                "action": "Just do it",
            },
            ResearchLevel.LIGHT: {
                "name": "LIGHT",
                "time": "30-60 seconds",
                "action": "Quick sanity check",
            },
            ResearchLevel.MODERATE: {
                "name": "MODERATE",
                "time": "2-5 minutes",
                "action": "Read code, check docs, verify assumptions",
            },
            ResearchLevel.DEEP: {
                "name": "DEEP",
                "time": "10-30 minutes",
                "action": "Multiple sources, cross-reference, pre-mortem",
            },
            ResearchLevel.ULTRATHINK: {
                "name": "ULTRATHINK",
                "time": "1+ hours",
                "action": "Exhaustive research, adversarial analysis",
            },
        }
        return descriptions.get(level, {})


# Singleton instance for Genesis
_router_instance = None


def get_research_router() -> ResearchRouter:
    """Get the singleton ResearchRouter instance."""
    global _router_instance
    if _router_instance is None:
        _router_instance = ResearchRouter()
    return _router_instance


def route_task(task: str) -> ResearchDecision:
    """
    Convenience function to route a task.

    Usage:
        decision = route_task("Implement new feature")
        if decision.level >= ResearchLevel.DEEP:
            # Do comprehensive research first
            pass
    """
    return get_research_router().route(task)


# CLI interface
if __name__ == "__main__":
    import sys

    if len(sys.argv) < 2:
        print("Usage: python research_router.py '<task description>'")
        print("\nExample:")
        print("  python research_router.py 'Implement new authentication system'")
        sys.exit(1)

    task = " ".join(sys.argv[1:])
    router = ResearchRouter()
    decision = router.route(task)

    print(f"\n{'='*60}")
    print(f"RESEARCH ROUTER DECISION")
    print(f"{'='*60}")
    print(f"\nTask: {task}")
    print(f"\nComplexity Factors:")
    print(f"  Reversibility: {decision.factors.reversibility}/3")
    print(f"  Blast Radius:  {decision.factors.blast_radius}/3")
    print(f"  Novelty:       {decision.factors.novelty}/3")
    print(f"  Confidence:    {decision.factors.confidence}/3")
    print(f"  Stakes:        {decision.factors.stakes}/3")
    print(f"  TOTAL:         {decision.factors.total_score}/15")

    desc = router.get_level_description(decision.level)
    print(f"\n>>> RESEARCH LEVEL: {desc['name']}")
    print(f"    Time Required: {desc['time']}")
    print(f"    Action: {desc['action']}")

    if decision.escalation_reasons:
        print(f"\n⚠️  ESCALATION TRIGGERS:")
        for reason in decision.escalation_reasons:
            print(f"    • {reason}")

    if decision.gate.checks:
        print(f"\n✓ GATES TO PASS:")
        for i, check in enumerate(decision.gate.checks):
            print(f"    [ ] {check}")

    print(f"\n{'='*60}\n")
