"""
cortex/core/task_mode.py — Task-Mode Classification (Issue #362).

Deterministic task-mode detection from query text and optional context.
No LLM calls — uses keyword signals, query pattern matching, and context
hints to classify queries into one of 8 task modes.

Task modes drive retrieval policy selection: each mode defines which
retrieval channels to query, budget allocation weights, confidence
thresholds, and result limits.

Modes:
    continue_project — resume ongoing work
    design           — architecture/planning
    write            — content creation
    plan             — strategic planning
    decide           — decision making
    debug            — troubleshooting
    recall_history   — explicit memory lookup
    future_commitment— scheduling/promises

Dependencies: None (leaf module).
Dependents: cortex.core.retrieval_policy, cortex.core.retrieval
"""

from __future__ import annotations

import logging
import re
from dataclasses import dataclass, field
from enum import Enum
from typing import Dict, List, Optional

logger = logging.getLogger(__name__)


class TaskMode(str, Enum):
    """Enumeration of supported task modes for retrieval policy routing."""

    CONTINUE_PROJECT = "continue_project"
    DESIGN = "design"
    WRITE = "write"
    PLAN = "plan"
    DECIDE = "decide"
    DEBUG = "debug"
    RECALL_HISTORY = "recall_history"
    FUTURE_COMMITMENT = "future_commitment"


@dataclass
class TaskModeResult:
    """Result of task-mode classification.

    Attributes:
        mode: Detected task mode.
        confidence: Classification confidence in [0.0, 1.0].
        signals: Keywords/patterns that triggered the classification.
    """

    mode: TaskMode
    confidence: float
    signals: List[str] = field(default_factory=list)


# ---------------------------------------------------------------------------
# Signal definitions — keyword lists and regex patterns per mode
# ---------------------------------------------------------------------------

# Each entry: (mode, keywords, patterns, base_weight)
# Keywords are checked as whole words (case-insensitive).
# Patterns are compiled regexes checked against the full query.
# base_weight: how much a single keyword match contributes to confidence.

_MODE_SIGNALS: List[tuple] = [
    (
        TaskMode.DEBUG,
        [
            "debug", "error", "bug", "fix", "crash", "exception", "traceback",
            "stacktrace", "failing", "broken", "issue", "troubleshoot",
            "diagnose", "logs", "stderr", "segfault", "panic",
        ],
        [
            re.compile(r"why (?:is|does|did) .+ (?:fail|crash|break|error)", re.I),
            re.compile(r"(?:what|where) (?:is|are) the (?:error|bug|issue)", re.I),
            re.compile(r"(?:not working|doesn'?t work|won'?t (?:start|run|compile))", re.I),
            re.compile(r"how (?:do I|to) fix", re.I),
        ],
        0.15,
    ),
    (
        TaskMode.DESIGN,
        [
            "design", "architecture", "architect", "spec", "specification",
            "rfc", "proposal", "interface", "api", "schema", "pattern",
            "abstraction", "module", "component", "layer", "tradeoff",
            "tradeoffs", "trade-off", "constraint", "constraints",
        ],
        [
            re.compile(r"how should (?:we|I) (?:design|architect|structure)", re.I),
            re.compile(r"what (?:pattern|approach|architecture)", re.I),
            re.compile(r"(?:system|software|data) design", re.I),
        ],
        0.12,
    ),
    (
        TaskMode.PLAN,
        [
            "plan", "planning", "roadmap", "milestone", "timeline",
            "strategy", "strategic", "goal", "goals", "objective",
            "objectives", "prioritize", "priorities", "quarter",
            "sprint", "backlog", "scope",
        ],
        [
            re.compile(r"what (?:should|do) (?:we|I) (?:do|work on) (?:next|first)", re.I),
            re.compile(r"(?:create|make|build) (?:a )?(?:plan|roadmap|timeline)", re.I),
            re.compile(r"what are (?:the|our) (?:priorities|goals|objectives)", re.I),
        ],
        0.12,
    ),
    (
        TaskMode.DECIDE,
        [
            "decide", "decision", "choose", "choice", "compare",
            "comparison", "versus", "vs", "pros", "cons", "tradeoff",
            "evaluate", "assessment", "recommend", "recommendation",
            "option", "options", "alternative", "alternatives",
        ],
        [
            re.compile(r"should (?:we|I) (?:use|go with|pick|choose)", re.I),
            re.compile(r"(?:which|what) (?:is|would be) (?:better|best)", re.I),
            re.compile(r"(?:pros and cons|advantages and disadvantages)", re.I),
            re.compile(r"(?:A|option \w+) (?:vs|versus|or) (?:B|option \w+)", re.I),
        ],
        0.12,
    ),
    (
        TaskMode.CONTINUE_PROJECT,
        [
            "continue", "resume", "pick up", "where were we",
            "last time", "ongoing", "in progress", "work on",
            "open loop", "unfinished", "pending",
        ],
        [
            re.compile(r"(?:where|what) (?:did|were) (?:we|I) (?:leave off|working on|doing)", re.I),
            re.compile(r"(?:continue|resume|pick up) (?:from|where)", re.I),
            re.compile(r"what (?:was|is) (?:left|remaining|pending|unfinished)", re.I),
        ],
        0.12,
    ),
    (
        TaskMode.WRITE,
        [
            "write", "writing", "draft", "compose", "blog", "post",
            "article", "essay", "documentation", "document", "readme",
            "copy", "content", "paragraph", "section", "style guide",
            "tone", "voice", "narrative",
        ],
        [
            re.compile(r"(?:write|draft|compose) (?:a|an|the|some)", re.I),
            re.compile(r"help (?:me )?write", re.I),
            re.compile(r"(?:create|generate) (?:content|documentation|a draft)", re.I),
        ],
        0.12,
    ),
    (
        TaskMode.RECALL_HISTORY,
        [
            "remember", "recall", "history", "what did", "when did",
            "last time", "previously", "past", "earlier", "before",
            "memory", "conversation", "discussed", "mentioned", "said",
            "told", "asked",
        ],
        [
            re.compile(r"(?:do you|can you) remember", re.I),
            re.compile(r"what (?:did (?:we|I|you)|was|were)", re.I),
            re.compile(r"(?:when|where|how) did (?:we|I|you)", re.I),
            re.compile(r"(?:have (?:we|I)|did (?:we|I)) (?:ever|already)", re.I),
        ],
        0.12,
    ),
    (
        TaskMode.FUTURE_COMMITMENT,
        [
            "deadline", "due", "schedule", "scheduled", "promise",
            "commitment", "committed", "appointment", "meeting",
            "remind", "reminder", "by when", "due date", "deliver",
            "deliverable", "eta", "ship date",
        ],
        [
            re.compile(r"(?:when|what) (?:is|are) .+ (?:due|deadline|scheduled)", re.I),
            re.compile(r"(?:remind me|don'?t forget|remember to)", re.I),
            re.compile(r"(?:what|any) (?:commitments|promises|deadlines)", re.I),
            re.compile(r"(?:I|we) (?:need to|have to|must|promised to)", re.I),
        ],
        0.12,
    ),
]


class TaskModeClassifier:
    """Deterministic task-mode classifier.

    Takes query text and optional context, returns a TaskModeResult with
    the detected mode, confidence score, and signals that triggered it.

    No LLM calls — classification is purely keyword/pattern based.
    """

    def __init__(self, default_mode: TaskMode = TaskMode.RECALL_HISTORY) -> None:
        self._default_mode = default_mode

    def classify(
        self,
        query: str,
        context: Optional[Dict[str, str]] = None,
    ) -> TaskModeResult:
        """Classify a query into a task mode.

        Args:
            query: Natural language query text.
            context: Optional dict with hints like 'task_hint', 'session_type',
                     'recent_activity' that may boost certain modes.

        Returns:
            TaskModeResult with mode, confidence, and matched signals.
        """
        if not query or not query.strip():
            return TaskModeResult(
                mode=self._default_mode,
                confidence=0.0,
                signals=["empty_query"],
            )

        query_lower = query.lower().strip()
        scores: Dict[TaskMode, float] = {}
        signals_map: Dict[TaskMode, List[str]] = {}

        for mode, keywords, patterns, base_weight in _MODE_SIGNALS:
            mode_signals: List[str] = []
            score = 0.0

            # Keyword matching (whole word, case-insensitive)
            for kw in keywords:
                # Use word boundary matching for multi-word keywords
                escaped = re.escape(kw)
                if re.search(rf"\b{escaped}\b", query_lower):
                    mode_signals.append(f"kw:{kw}")
                    score += base_weight

            # Pattern matching
            for pat in patterns:
                if pat.search(query):
                    mode_signals.append(f"pat:{pat.pattern[:40]}")
                    score += base_weight * 1.5  # patterns are stronger signals

            # Context hints
            if context:
                hint = context.get("task_hint", "").lower()
                if hint and hint == mode.value:
                    mode_signals.append("ctx:task_hint")
                    score += 0.3

                session_type = context.get("session_type", "").lower()
                if session_type:
                    _session_boosts = {
                        "coding": [TaskMode.DEBUG, TaskMode.DESIGN, TaskMode.CONTINUE_PROJECT],
                        "writing": [TaskMode.WRITE],
                        "planning": [TaskMode.PLAN, TaskMode.DECIDE],
                    }
                    if mode in _session_boosts.get(session_type, []):
                        mode_signals.append(f"ctx:session_{session_type}")
                        score += 0.1

            if mode_signals:
                scores[mode] = score
                signals_map[mode] = mode_signals

        if not scores:
            return TaskModeResult(
                mode=self._default_mode,
                confidence=0.1,
                signals=["no_signals_matched"],
            )

        # Pick mode with highest score
        best_mode = max(scores, key=lambda m: scores[m])
        raw_score = scores[best_mode]

        # Normalize confidence to [0.0, 1.0] using a sigmoid-like curve
        # Single keyword match (~0.12) → ~0.3 confidence
        # Two keywords (~0.24) → ~0.5 confidence
        # Keyword + pattern (~0.30) → ~0.6 confidence
        # Multiple signals (~0.50+) → ~0.8+ confidence
        confidence = min(1.0, raw_score / (raw_score + 0.25))

        # Check for ambiguity: if second-best is close, reduce confidence
        sorted_modes = sorted(scores.items(), key=lambda x: -x[1])
        if len(sorted_modes) >= 2:
            gap = sorted_modes[0][1] - sorted_modes[1][1]
            if gap < 0.05:
                confidence *= 0.7  # significant ambiguity penalty

        logger.debug(
            "TaskModeClassifier: query=%r → mode=%s confidence=%.2f signals=%s",
            query[:80],
            best_mode.value,
            confidence,
            signals_map.get(best_mode, []),
        )

        return TaskModeResult(
            mode=best_mode,
            confidence=round(confidence, 3),
            signals=signals_map.get(best_mode, []),
        )
