#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
"""
Canonical enum types for PULSE brain subsystems.

Replaces 40+ files using magic string literals for knowledge types,
validity states, task statuses, and competence levels. Import these
instead of hardcoding strings.

Usage:
    from pulse.core.enums import KnowledgeType, Validity, TaskStatus
    entry["type"] = KnowledgeType.LESSON          # "lesson"
    entry["validity"] = Validity.VALID             # "valid"
    if status == TaskStatus.OPEN: ...              # "open"
"""
from enum import Enum


class _StrEnum(str, Enum):
    """String enum base — values compare equal to plain strings.

    Python 3.10 doesn't have StrEnum, so we polyfill.
    `KnowledgeType.LESSON == "lesson"` → True
    `json.dumps({"type": KnowledgeType.LESSON})` → '{"type": "lesson"}'
    """

    def __str__(self) -> str:
        return self.value

    def __repr__(self) -> str:
        return f"{self.__class__.__name__}.{self.name}"


# ── Knowledge Types ──────────────────────────────────────────────

class KnowledgeType(_StrEnum):
    """Types of knowledge stored in Hippocampus JSONL/JSON files."""
    LESSON      = "lesson"
    FAILURE     = "failure"
    PATTERN     = "pattern"
    PRIVATE     = "private"
    FACT        = "fact"
    SCHEMA      = "schema"
    PROCEDURE   = "procedure"
    DECISION    = "decision"
    INTENT      = "intent"
    ANTI_PATTERN = "anti_pattern"
    EPISODIC    = "episodic"
    PREDICTION  = "prediction"


# Lookup sets for fast membership checks
KNOWLEDGE_TYPES: frozenset[str] = frozenset(kt.value for kt in KnowledgeType)

# Prefix map for ID generation (L-abc123, F-abc123, etc.)
KNOWLEDGE_TYPE_PREFIX: dict[str, str] = {
    KnowledgeType.LESSON:       "L",
    KnowledgeType.FAILURE:      "F",
    KnowledgeType.PATTERN:      "P",
    KnowledgeType.PRIVATE:      "V",
    KnowledgeType.FACT:         "K",
    KnowledgeType.SCHEMA:       "S",
    KnowledgeType.PROCEDURE:    "R",
    KnowledgeType.DECISION:     "D",
    KnowledgeType.INTENT:       "I",
    KnowledgeType.ANTI_PATTERN: "A",
    KnowledgeType.EPISODIC:     "E",
    KnowledgeType.PREDICTION:   "X",
}


def validate_knowledge_type(value: str) -> str:
    """Validate and normalize a knowledge type string.

    Returns the canonical string value, or raises ValueError.
    Accepts both enum members and raw strings.
    """
    if isinstance(value, KnowledgeType):
        return value.value
    if value in KNOWLEDGE_TYPES:
        return value
    raise ValueError(
        f"Invalid knowledge type: {value!r}. "
        f"Valid types: {sorted(KNOWLEDGE_TYPES)}"
    )


# ── Validity States ──────────────────────────────────────────────
# Mirrors temporal.py constants but as an enum with transition validation.

class Validity(_StrEnum):
    """Validity states for brain knowledge items."""
    VALID       = "valid"
    STABLE      = "stable"      # Item 49 (P13): E-LTP->L-LTP intermediate state
    CONTESTED   = "contested"
    SUPERSEDED  = "superseded"
    EXPIRED     = "expired"
    HISTORICAL  = "historical"
    PERMANENT   = "permanent"   # Flashbulb memory — high-emotion + high-salience


VALIDITY_STATES: frozenset[str] = frozenset(v.value for v in Validity)

# Legal transitions: {from_state: {to_states}}
VALIDITY_TRANSITIONS: dict[str, frozenset[str]] = {
    Validity.VALID:      frozenset({Validity.STABLE, Validity.SUPERSEDED, Validity.EXPIRED, Validity.HISTORICAL, Validity.CONTESTED, Validity.PERMANENT}),
    Validity.STABLE:     frozenset({Validity.SUPERSEDED, Validity.HISTORICAL, Validity.CONTESTED, Validity.PERMANENT}),  # Item 49: stable can be superseded
    Validity.CONTESTED:  frozenset({Validity.VALID, Validity.HISTORICAL}),
    Validity.SUPERSEDED: frozenset({Validity.HISTORICAL}),
    Validity.EXPIRED:    frozenset({Validity.HISTORICAL}),
    Validity.HISTORICAL: frozenset(),  # Terminal state
    Validity.PERMANENT:  frozenset(),  # Terminal state — permanent memory never degrades
}


def validate_validity(value: str) -> str:
    """Validate a validity state string. Returns canonical value or raises."""
    if isinstance(value, Validity):
        return value.value
    if value in VALIDITY_STATES:
        return value
    raise ValueError(
        f"Invalid validity state: {value!r}. "
        f"Valid states: {sorted(VALIDITY_STATES)}"
    )


def is_valid_transition(from_state: str, to_state: str) -> bool:
    """Check if a validity state transition is legal."""
    allowed = VALIDITY_TRANSITIONS.get(from_state, frozenset())
    return to_state in allowed


# ── Task Status ──────────────────────────────────────────────────

class TaskStatus(_StrEnum):
    """Task board statuses (state/task_board.json)."""
    OPEN        = "open"
    CLAIMED     = "claimed"
    IN_PROGRESS = "in_progress"
    BLOCKED     = "blocked"
    DONE        = "done"
    COMPLETED   = "completed"   # Alias used by some paths
    FAILED      = "failed"
    ARCHIVED    = "archived"


# Terminal states — task is finished and won't transition further
TERMINAL_TASK_STATES: frozenset[str] = frozenset({
    TaskStatus.DONE, TaskStatus.COMPLETED,
    TaskStatus.FAILED, TaskStatus.ARCHIVED,
})

ACTIVE_TASK_STATES: frozenset[str] = frozenset({
    TaskStatus.OPEN, TaskStatus.CLAIMED,
    TaskStatus.IN_PROGRESS, TaskStatus.BLOCKED,
})


# ── Competence Levels ────────────────────────────────────────────
# Used by metamemory.py for domain knowledge assessment.

class CompetenceLevel(_StrEnum):
    """Domain competence levels computed by metamemory."""
    BLIND_SPOT  = "blind_spot"
    NOVICE      = "novice"
    COMPETENT   = "competent"
    EXPERT      = "expert"


# ── Agent Tiers ──────────────────────────────────────────────────

class AgentTier(_StrEnum):
    """Agent capability tiers for task matching."""
    FAST     = "fast"       # Haiku, Flash — simple tasks
    STANDARD = "standard"   # Sonnet, Gemini Pro — features
    PREMIUM  = "premium"    # Opus, GPT-4 — architecture


# ── Event Bus Channels ───────────────────────────────────────────
# Not an enum (channels are open-ended), but canonical constants
# for the high-traffic channels that need subscribers wired.

class EventChannel:
    """Known event bus channel names."""
    # Memory lifecycle
    MEMORY_REINFORCE    = "memory.reconsolidation.reinforce"
    MEMORY_CONTRADICT   = "memory.reconsolidation.contradict"
    MEMORY_RESURRECTION = "memory.resurrection"

    # Knowledge pipeline
    PATTERN_EVOLVED     = "pattern.evolved"
    PATTERN_DEPRECATED  = "pattern.deprecated"
    CONSOLIDATION_DEEP  = "consolidation.deep.complete"
    WORKSPACE_BROADCAST = "workspace.broadcast"

    # System health
    SENTINEL_ALERT      = "sentinel.alert"
    INTEROCEPTION_ALERT = "interoception.alert"
    CONSCIENCE_DRIFT    = "conscience.drift"
    CONSCIENCE_WRITE    = "conscience.brain_write"

    # Task/decision lifecycle
    DECISION_OPENED     = "decision.opened"
    DECISION_SYNTHESIZED = "decision.synthesized"
    APPROVAL_NEEDED     = "approval.needed"
    GOAL_APPROVED       = "goal.approved"

    # Replay/consolidation
    REPLAY_COMPLETE     = "replay.complete"
    CULTURE_SYNTHESIZED = "culture.synthesized"
