"""Cognitive bytecode definitions.

The bytecode is the stable interface between any controller and the runtime.
A controller may be heuristic, LLM-backed, or neural, but it must emit valid
Instruction objects.
"""

from __future__ import annotations

from dataclasses import dataclass, field
from enum import StrEnum
from typing import Any
from uuid import uuid4


class CognitiveOp(StrEnum):
    PARSE_INPUT = "PARSE_INPUT"
    CLASSIFY_TASK = "CLASSIFY_TASK"
    CREATE_GOAL = "CREATE_GOAL"
    CREATE_SUBGOAL = "CREATE_SUBGOAL"
    CREATE_HYPOTHESIS = "CREATE_HYPOTHESIS"
    RETRIEVE_MEMORY = "RETRIEVE_MEMORY"
    RETRIEVE_SKILL = "RETRIEVE_SKILL"
    CALL_TOOL = "CALL_TOOL"
    RUN_TEST = "RUN_TEST"
    SIMULATE = "SIMULATE"
    COMPARE = "COMPARE"
    REFUTE = "REFUTE"
    VERIFY = "VERIFY"
    PATCH = "PATCH"
    GENERALIZE = "GENERALIZE"
    STORE_EPISODE = "STORE_EPISODE"
    STORE_FAILURE = "STORE_FAILURE"
    CREATE_SKILL = "CREATE_SKILL"
    UPDATE_SKILL = "UPDATE_SKILL"
    PRUNE_BRANCH = "PRUNE_BRANCH"
    MERGE_BRANCHES = "MERGE_BRANCHES"
    ANSWER = "ANSWER"


_TERMINAL_OPS = {CognitiveOp.ANSWER, CognitiveOp.STORE_EPISODE, CognitiveOp.STORE_FAILURE}


@dataclass(frozen=True, slots=True)
class Instruction:
    """A single cognitive bytecode instruction."""

    op: CognitiveOp
    args: dict[str, Any] = field(default_factory=dict)
    expected_signal: str | None = None
    budget: int = 1
    rationale: str = ""
    instruction_id: str = field(default_factory=lambda: uuid4().hex)

    def __post_init__(self) -> None:
        if not isinstance(self.op, CognitiveOp):
            object.__setattr__(self, "op", CognitiveOp(str(self.op)))
        if self.budget < 0:
            raise ValueError("Instruction budget must be non-negative")

    @property
    def is_terminal(self) -> bool:
        return self.op in _TERMINAL_OPS

    def to_dict(self) -> dict[str, Any]:
        return {
            "instruction_id": self.instruction_id,
            "op": self.op.value,
            "args": self.args,
            "expected_signal": self.expected_signal,
            "budget": self.budget,
            "rationale": self.rationale,
        }

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "Instruction":
        return cls(
            instruction_id=str(data.get("instruction_id") or uuid4().hex),
            op=CognitiveOp(str(data["op"])),
            args=dict(data.get("args") or {}),
            expected_signal=data.get("expected_signal"),
            budget=int(data.get("budget", 1)),
            rationale=str(data.get("rationale", "")),
        )


def validate_program(program: list[Instruction]) -> None:
    """Validate a sequence of bytecode instructions.

    This is intentionally conservative. It verifies shape, not semantic
    correctness.
    """

    if not program:
        raise ValueError("Program must contain at least one instruction")
    for instruction in program:
        if not isinstance(instruction, Instruction):
            raise TypeError("Program entries must be Instruction instances")
    if not any(item.is_terminal for item in program):
        raise ValueError("Program must contain a terminal instruction")
