"""Gate 11A deterministic dream consolidation v2."""

from __future__ import annotations

import json
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any

from .consolidation import ConsolidatedMemoryRecord, MemoryConsolidator, MemoryRetriever
from .controller_v2_dataset import ControllerV2DatasetManifest, build_controller_v2_dataset, rebuild_validate_controller_v2_dataset
from .dream import DreamReplayTask
from .ledger import TraceLedger, TraceLedgerRecord, stable_hash
from .skills import Skill

GATE11A_TRACE_DOMAINS = ("coding", "real_repo", "document", "planning", "memory", "skill_graph", "controller")
GATE11A_TRACES_PER_DOMAIN = 72
GATE11A_MIN_TRACE_COUNT = 500
GATE11A_REPLAY_TASK_TARGET = 120
GATE11A_ADVERSARIAL_TASK_TARGET = 60
GATE11A_DREAM_V2_EVAL_FILE = "gate11a_dream_v2_eval.json"


@dataclass(slots=True)
class DreamV2Artifacts:
    trace_count: int
    domain_counts: dict[str, int]
    replay_tasks: list[DreamReplayTask]
    adversarial_tasks: list[DreamReplayTask]
    skill_update_candidates: list[dict[str, object]]
    memory_invalidation_candidates: list[dict[str, object]]
    memory_records: list[ConsolidatedMemoryRecord]
    retrieval_precision: float
    unsupported_memory_count: int
    controller_manifest: ControllerV2DatasetManifest
    controller_refresh: dict[str, object]
    paths: dict[str, str]

    def to_dict(self) -> dict[str, object]:
        return {
            "trace_count": self.trace_count,
            "domain_counts": dict(self.domain_counts),
            "replay_tasks": [task.to_dict() for task in self.replay_tasks],
            "adversarial_tasks": [task.to_dict() for task in self.adversarial_tasks],
            "skill_update_candidates": [dict(item) for item in self.skill_update_candidates],
            "memory_invalidation_candidates": [dict(item) for item in self.memory_invalidation_candidates],
            "memory_records": [record.to_dict() for record in self.memory_records],
            "retrieval_precision": self.retrieval_precision,
            "unsupported_memory_count": self.unsupported_memory_count,
            "controller_manifest": self.controller_manifest.to_dict(),
            "controller_refresh": dict(self.controller_refresh),
            "paths": dict(self.paths),
        }


def build_gate11a_dream_v2_artifacts(state_dir: str | Path) -> DreamV2Artifacts:
    base_dir = Path(state_dir)
    base_dir.mkdir(parents=True, exist_ok=True)
    ledger = TraceLedger(base_dir, file_name="gate11a_trace_ledger.jsonl")
    traces = build_gate11a_trace_corpus()
    for trace in traces:
        ledger.append(trace)
    memory_records, consolidation_report = MemoryConsolidator().consolidate(
        traces,
        bad_candidate_decisions=_gate11a_bad_candidate_decisions(),
    )
    memory_path = base_dir / "gate11a_memory.json"
    memory_path.write_text(
        json.dumps([record.to_dict() for record in memory_records], indent=2, sort_keys=True),
        encoding="utf-8",
    )
    replay_tasks = _build_gate11a_replay_tasks(memory_records)
    adversarial_tasks = _build_gate11a_adversarial_tasks(memory_records)
    skill_update_candidates = _build_gate11a_skill_update_candidates(memory_records)
    memory_invalidation_candidates = _build_gate11a_memory_invalidation_candidates(memory_records)
    retriever = MemoryRetriever(memory_records)
    retriever.retrieve("verified source repair planning document controller skill evidence anti-pattern", limit=64)
    controller_manifest = build_controller_v2_dataset(base_dir / "controller_refresh")
    controller_refresh = rebuild_validate_controller_v2_dataset(controller_manifest, base_dir / "controller_refresh_rebuild")
    replay_path = base_dir / "gate11a_replay_tasks.jsonl"
    adversarial_path = base_dir / "gate11a_adversarial_tasks.jsonl"
    skill_candidates_path = base_dir / "gate11a_skill_update_candidates.json"
    invalidation_path = base_dir / "gate11a_memory_invalidation_candidates.json"
    _write_jsonl(replay_path, (task.to_dict() for task in replay_tasks))
    _write_jsonl(adversarial_path, (task.to_dict() for task in adversarial_tasks))
    skill_candidates_path.write_text(json.dumps(skill_update_candidates, indent=2, sort_keys=True), encoding="utf-8")
    invalidation_path.write_text(json.dumps(memory_invalidation_candidates, indent=2, sort_keys=True), encoding="utf-8")
    return DreamV2Artifacts(
        trace_count=len(traces),
        domain_counts={domain: sum(1 for trace in traces if trace.evaluator_metadata.get("domain") == domain) for domain in GATE11A_TRACE_DOMAINS},
        replay_tasks=replay_tasks,
        adversarial_tasks=adversarial_tasks,
        skill_update_candidates=skill_update_candidates,
        memory_invalidation_candidates=memory_invalidation_candidates,
        memory_records=memory_records,
        retrieval_precision=retriever.report.retrieval_precision,
        unsupported_memory_count=int(consolidation_report.unsupported_memory_count + retriever.report.unsupported_memory_count),
        controller_manifest=controller_manifest,
        controller_refresh=controller_refresh,
        paths={
            "ledger_path": str(ledger.path),
            "memory_path": str(memory_path),
            "replay_tasks_path": str(replay_path),
            "adversarial_tasks_path": str(adversarial_path),
            "skill_update_candidates_path": str(skill_candidates_path),
            "memory_invalidation_candidates_path": str(invalidation_path),
            "controller_manifest_path": controller_manifest.manifest_path,
            "controller_dataset_path": controller_manifest.dataset_path,
            "controller_rebuild_manifest_path": str(controller_refresh.get("rebuilt_manifest_path", "")),
        },
    )


def build_gate11a_trace_corpus() -> list[TraceLedgerRecord]:
    traces: list[TraceLedgerRecord] = []
    patch_patterns = ("missing_import", "guard_clause", "schema_round_trip", "verifier_allowlist", "path_containment")
    for domain_index, domain in enumerate(GATE11A_TRACE_DOMAINS):
        for index in range(GATE11A_TRACES_PER_DOMAIN):
            success = index % 9 != 0
            split = "negative" if index % 10 == 0 else ("adversarial" if index % 7 == 0 else "train")
            pattern = patch_patterns[(index + domain_index) % len(patch_patterns)]
            trace = TraceLedgerRecord(
                trace_id="",
                schema_version=1,
                agent_visible={
                    "task_description": f"{domain} local consolidation trace {index:03d}",
                    "step_summaries": [
                        f"inspect {domain} inputs",
                        f"apply deterministic {pattern} repair",
                        "run compileall and pytest verifiers",
                    ],
                    "visible_tool_summaries": [
                        {"operation": "read_file", "success": True, "summary": f"inspected {domain} fixture"},
                        {"operation": "run_command", "success": success, "summary": "approved verifier completed"},
                    ],
                },
                evaluator_metadata={
                    "domain": domain,
                    "split": split,
                    "fixture_family": f"{domain}_family_{index % 5}",
                    "candidate_origin": "gate11a_fixture",
                },
                bytecode_ops=["OBSERVE", "PLAN", "VERIFY"],
                tool_calls=[
                    {
                        "operation": "run_command",
                        "args": {"command": ["python", "-m", "compileall", "src", "tests"]},
                        "success": success,
                        "summary": "compileall verifier",
                    },
                    {
                        "operation": "run_command",
                        "args": {"command": ["python", "-m", "pytest", "-q"]},
                        "success": success,
                        "summary": "pytest verifier",
                    },
                ],
                files_read=[f"{domain}/src/module_{index % 4}.py"],
                files_patched=[{"path": f"{domain}/src/module_{index % 4}.py", "pattern": pattern}] if success else [],
                verifier_commands=[["python", "-m", "compileall", "src", "tests"], ["python", "-m", "pytest", "-q"]],
                verifier_outputs=[
                    {"command": "compileall", "passed": success, "stdout_tail": "compiled"},
                    {"command": "pytest", "passed": success, "stdout_tail": "passed" if success else "failed"},
                ],
                success=success,
                reused_skill_id=f"skill_{domain}_{index % 6}" if success and index % 3 == 0 else None,
                fallback_used=not success and index % 2 == 0,
                quarantined_skill_id=f"quarantine_{domain}_{index % 4}" if split == "negative" else None,
                created_at=f"2026-06-18T00:{domain_index:02d}:{index % 60:02d}Z",
            )
            trace.content_hash = stable_hash(trace._hash_payload())
            trace.trace_id = "trace_" + trace.content_hash[:24]
            traces.append(trace)
    return traces


def _build_gate11a_replay_tasks(memories: list[ConsolidatedMemoryRecord]) -> list[DreamReplayTask]:
    source_ids = [memory.memory_id for memory in memories if memory.status == "active"] or ["gate11a_memory"]
    tasks: list[DreamReplayTask] = []
    for index in range(GATE11A_REPLAY_TASK_TARGET):
        source_id = source_ids[index % len(source_ids)]
        domain = GATE11A_TRACE_DOMAINS[index % len(GATE11A_TRACE_DOMAINS)]
        tasks.append(
            DreamReplayTask(
                task_id=f"gate11a_replay_{index:03d}",
                task_kind="replay",
                source_memory_ids=[source_id],
                description=f"Replay verified {domain} consolidation behavior from evidence-backed memory.",
                metadata={"domain": domain, "gate": "11A", "source": "dream_v2"},
            )
        )
    return tasks


def _build_gate11a_adversarial_tasks(memories: list[ConsolidatedMemoryRecord]) -> list[DreamReplayTask]:
    warning_ids = [memory.memory_id for memory in memories if memory.memory_type in {"anti_pattern", "false_trigger_warning", "quarantine_reason"}]
    source_ids = warning_ids or [memory.memory_id for memory in memories] or ["gate11a_warning"]
    tasks: list[DreamReplayTask] = []
    for index in range(GATE11A_ADVERSARIAL_TASK_TARGET):
        source_id = source_ids[index % len(source_ids)]
        domain = GATE11A_TRACE_DOMAINS[index % len(GATE11A_TRACE_DOMAINS)]
        tasks.append(
            DreamReplayTask(
                task_id=f"gate11a_adversarial_{index:03d}",
                task_kind="adversarial",
                source_memory_ids=[source_id],
                description=f"Reject unsafe or unsupported {domain} lookalike behavior during dream replay.",
                metadata={"domain": domain, "gate": "11A", "source": "dream_v2", "must_abstain": True},
            )
        )
    return tasks


def _build_gate11a_skill_update_candidates(memories: list[ConsolidatedMemoryRecord]) -> list[dict[str, object]]:
    candidates: list[dict[str, object]] = []
    evidence = [memory for memory in memories if memory.memory_type in {"skill_evidence", "repair_pattern"}]
    for index, memory in enumerate(evidence[:24]):
        skill = Skill(
            name=f"gate11a_candidate_skill_{index:02d}",
            description=f"Candidate skill update from {memory.memory_type}; approval required before promotion.",
            triggers=[memory.memory_type, "gate11a", "verified"],
            steps=["inspect local evidence", "run approved verifier", "record rollback evidence"],
            verifier="python -m pytest -q",
            promoted=False,
            status="candidate",
            promotion_evidence_count=len(memory.source_trace_ids),
            metadata={"source_memory_id": memory.memory_id, "approval_required": True, "gate": "11A"},
        )
        candidates.append({"candidate_id": "skill_update_" + stable_hash(skill.to_dict())[:16], "skill": skill.to_dict(), "approved": False})
    return candidates


def _build_gate11a_memory_invalidation_candidates(memories: list[ConsolidatedMemoryRecord]) -> list[dict[str, object]]:
    candidates: list[dict[str, object]] = []
    for memory in memories:
        if memory.memory_type not in {"failure_episode", "anti_pattern", "false_trigger_warning", "quarantine_reason"}:
            continue
        payload = {
            "memory_id": memory.memory_id,
            "reason": f"Gate 11A adversarial consolidation review for {memory.memory_type}",
            "source_trace_hashes": list(memory.source_trace_hashes),
            "approval_required": True,
            "applied": False,
        }
        candidates.append({"candidate_id": "memory_invalidation_" + stable_hash(payload)[:16], **payload})
        if len(candidates) >= 32:
            break
    return candidates


def _gate11a_bad_candidate_decisions() -> list[dict[str, object]]:
    return [
        {"kind": "unsupported_doc_answer", "reason": "document answer lacked cited local source"},
        {"kind": "unsafe_repo_mutation", "reason": "repo change attempted outside isolated worktree"},
        {"kind": "unapproved_planning_side_effect", "reason": "planning step requested side effect without approval"},
        {"kind": "controller_mask_escape", "reason": "learned candidate tried to select a masked action"},
    ]


def _write_jsonl(path: Path, rows: object) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", encoding="utf-8") as handle:
        for row in rows:
            handle.write(json.dumps(row, sort_keys=True) + "\n")
