"""Dream consolidation for candidate skill induction."""

from __future__ import annotations

from dataclasses import dataclass, field

from .consolidation import ConsolidatedMemoryRecord
from .memory import EpisodeMemory
from .skills import Skill, induce_skill_from_episodes
from .workbench import AgentTaskView, TaskVariant


@dataclass(slots=True)
class DreamReport:
    sampled_episodes: int
    candidate_skill: Skill | None
    promoted: bool = False
    summary: str = ""

    def to_dict(self) -> dict[str, object]:
        return {
            "sampled_episodes": self.sampled_episodes,
            "candidate_skill": self.candidate_skill.to_dict() if self.candidate_skill else None,
            "promoted": self.promoted,
            "summary": self.summary,
        }


class DreamConsolidator:
    """Conservative consolidation pass.

    The MVP only proposes skills. Promotion requires a separate verifier gate.
    """

    def __init__(self, memory: EpisodeMemory) -> None:
        self.memory = memory

    def run_once(self, min_support: int = 2) -> DreamReport:
        episodes = self.memory.all_episodes()
        candidate = induce_skill_from_episodes(episodes, min_support=min_support)
        if candidate is None:
            return DreamReport(
                sampled_episodes=len(episodes),
                candidate_skill=None,
                promoted=False,
                summary="No repeated successful trace pattern met support threshold.",
            )
        return DreamReport(
            sampled_episodes=len(episodes),
            candidate_skill=candidate,
            promoted=False,
            summary=f"Proposed candidate skill {candidate.name}; not promoted without verifier gate.",
        )


@dataclass(slots=True)
class DreamReplayTask:
    task_id: str
    task_kind: str
    source_memory_ids: list[str]
    description: str
    evaluator_variant: TaskVariant | None = None
    agent_view: AgentTaskView | None = None
    metadata: dict[str, object] = field(default_factory=dict)

    def to_dict(self) -> dict[str, object]:
        return {
            "task_id": self.task_id,
            "task_kind": self.task_kind,
            "source_memory_ids": self.source_memory_ids,
            "description": self.description,
            "evaluator_variant": self.evaluator_variant.to_dict() if self.evaluator_variant else None,
            "agent_view": self.agent_view.to_dict() if self.agent_view else None,
            "metadata": self.metadata,
        }

    @classmethod
    def from_dict(cls, data: dict[str, object]) -> "DreamReplayTask":
        return cls(
            task_id=str(data["task_id"]),
            task_kind=str(data["task_kind"]),
            source_memory_ids=[str(item) for item in data.get("source_memory_ids", [])],
            description=str(data.get("description", "")),
            evaluator_variant=TaskVariant.from_dict(dict(data["evaluator_variant"])) if data.get("evaluator_variant") else None,
            agent_view=AgentTaskView.from_dict(dict(data["agent_view"])) if data.get("agent_view") else None,
            metadata=dict(data.get("metadata") or {}),
        )


class DreamTaskGenerator:
    """Deterministic explicit replay task generation for Gate 4."""

    def generate(self, memories: list[ConsolidatedMemoryRecord]) -> list[DreamReplayTask]:
        tasks: list[DreamReplayTask] = []
        kind_by_memory = {
            "repair_pattern": "replay",
            "success_episode": "replay",
            "false_trigger_warning": "adversarial",
            "anti_pattern": "negative",
            "quarantine_reason": "quarantine",
            "skill_evidence": "heldout",
        }
        counters: dict[str, int] = {}
        for memory in sorted(memories, key=lambda item: item.memory_id):
            task_kind = kind_by_memory.get(memory.memory_type)
            if task_kind is None:
                continue
            counters[task_kind] = counters.get(task_kind, 0) + 1
            tasks.append(
                DreamReplayTask(
                    task_id=f"dream_{task_kind}_{counters[task_kind]:02d}",
                    task_kind=task_kind,
                    source_memory_ids=[memory.memory_id],
                    description=f"Replay a local verified {task_kind} task from consolidated trace evidence.",
                    metadata={"memory_type": memory.memory_type},
                )
            )
        return _pad_replay_tasks(tasks, memories)


def _pad_replay_tasks(tasks: list[DreamReplayTask], memories: list[ConsolidatedMemoryRecord]) -> list[DreamReplayTask]:
    if not memories:
        return tasks
    requirements = {"replay": 3, "adversarial": 4, "negative": 4, "quarantine": 1}
    by_kind: dict[str, list[DreamReplayTask]] = {}
    for task in tasks:
        by_kind.setdefault(task.task_kind, []).append(task)
    source = memories[0].memory_id
    for kind, count in requirements.items():
        while len(by_kind.get(kind, [])) < count:
            next_index = len(by_kind.get(kind, [])) + 1
            task = DreamReplayTask(
                task_id=f"dream_{kind}_pad_{next_index:02d}",
                task_kind=kind,
                source_memory_ids=[source],
                description=f"Deterministic {kind} replay task derived from consolidated evidence.",
                metadata={"padded": True},
            )
            tasks.append(task)
            by_kind.setdefault(kind, []).append(task)
    return sorted(tasks, key=lambda item: item.task_id)
