"""Tests for brain stage/posture state model, L1 gates, and execution brief."""

from __future__ import annotations

import subprocess
import sys
import tempfile
import unittest
from pathlib import Path

from brain import mcp_server
from brain.state_model import topics_dir, topic_root, validate_topic_slug


class TopicRootResolutionTests(unittest.TestCase):
    def test_repo_root_with_topics_dir_resolves_topic_inside_topics_folder(self):
        with tempfile.TemporaryDirectory() as tmp:
            repo_root = Path(tmp)
            (repo_root / "topics" / "demo-topic").mkdir(parents=True)
            resolved = topic_root(repo_root, "demo-topic")
            self.assertEqual(resolved, repo_root / "topics" / "demo-topic")

    def test_direct_topics_root_still_resolves_existing_topic(self):
        with tempfile.TemporaryDirectory() as tmp:
            topics_root = Path(tmp)
            (topics_root / "demo-topic").mkdir()
            resolved = topic_root(topics_root, "demo-topic")
            self.assertEqual(resolved, topics_root / "demo-topic")


class BootstrapL1ScaffoldTests(unittest.TestCase):
    def test_bootstrap_topic_creates_l1_artifacts_and_new_state_fields(self):
        with tempfile.TemporaryDirectory() as tmp:
            repo_root = Path(tmp)
            (repo_root / "topics").mkdir()
            mcp_server.aitp_bootstrap_topic(
                topics_root=str(repo_root), topic_slug="demo-topic",
                title="Demo Topic", question="What is the bounded question?",
            )
            tr = repo_root / "topics" / "demo-topic"
            for name in [
                "question_contract.md", "source_basis.md", "convention_snapshot.md",
                "derivation_anchor_map.md", "contradiction_register.md",
                "source_toc_map.md",
            ]:
                self.assertTrue((tr / "L1" / name).exists(), f"Missing L1/{name}")
            fm, _ = mcp_server._parse_md(tr / "state.md")
            self.assertEqual(fm["status"], "new")
            self.assertEqual(fm["stage"], "L0")
            self.assertEqual(fm["posture"], "discover")
            self.assertEqual(fm["lane"], "unspecified")


class L1GateTests(unittest.TestCase):
    def _bootstrap(self, tmp):
        repo_root = Path(tmp)
        (repo_root / "topics").mkdir()
        mcp_server.aitp_bootstrap_topic(
            topics_root=str(repo_root), topic_slug="demo-topic",
            title="Demo Topic", question="What is the bounded question?",
        )
        return repo_root

    def test_execution_brief_blocks_on_first_missing_l0_field(self):
        with tempfile.TemporaryDirectory() as tmp:
            self._bootstrap(tmp)
            brief = mcp_server.aitp_get_execution_brief(topics_root=tmp, topic_slug="demo-topic")
            self.assertEqual(brief["stage"], "L0")
            self.assertEqual(brief["posture"], "discover")
            self.assertEqual(brief["gate_status"], "blocked_missing_field")
            self.assertTrue(
                "source_registry" in brief.get("required_artifact_path", "")
                or "source" in str(brief.get("missing_requirements", [])),
                f"Expected source-related missing requirement, got: {brief}",
            )

    def test_execution_brief_turns_ready_after_all_l1_artifacts_are_filled(self):
        with tempfile.TemporaryDirectory() as tmp:
            repo_root = self._bootstrap(tmp)
            tr = repo_root / "topics" / "demo-topic"

            # Satisfy L0 gate: fill source_registry and register a source
            mcp_server._write_md(
                tr / "L0" / "source_registry.md",
                {"artifact_kind": "l0_source_registry", "stage": "L0",
                 "source_count": 1, "search_status": "complete"},
                "# Source Registry\n\n## Search Methodology\narxiv\n\n## Source Inventory\npaper-a\n\n## Coverage Assessment\nAdequate.\n\n## Overall Verdict\nThis assessment confirms that the registered sources are sufficient for the bounded research question. Coverage spans the core derivation path and primary validation checks. No blocking gaps were identified. Source quality is adequate for current research intensity level.\n\n## Gaps And Next Sources\nNone\n\n## Prior L2 Knowledge\nNo prior L2 knowledge for this test bootstrap topic. Unit test fixture for gate verification.\n",
            )
            mcp_server._write_md(
                tr / "L0" / "sources" / "paper-a.md",
                {"artifact_kind": "l0_source", "source_type": "paper",
                 "slug": "paper-a", "short_title": "Paper A"},
                "# Paper A\n\nA source.\n",
            )
            mcp_server.aitp_advance_to_l1(topics_root=tmp, topic_slug="demo-topic")

            mcp_server._write_md(
                tr / "L1" / "question_contract.md",
                {"artifact_kind": "l1_question_contract", "stage": "L1",
                 "bounded_question": "What quantity is bounded here?",
                 "scope_boundaries": "One model, one regime. Does NOT consider multi-band effects or finite-temperature corrections.",
                 "target_quantities": "Gap and symmetry sector.",
                 "competing_hypotheses": "Alternative: the gap closes at the BKT transition point."},
                "# Question Contract\n\n## Bounded Question\nWhat quantity is bounded here?\n\n## Competing Hypotheses\nAlternative: the gap closes at the BKT transition point.\n\n## Scope Boundaries\nOne model, one regime. Does NOT consider multi-band effects or finite-temperature corrections.\n\n## Target Quantities Or Claims\nGap and symmetry sector.\n\n## Non-Success Conditions\nNo broad universality claim.\n\n## Uncertainty Markers\nFinite-size risk.\n\n## L2 Cross-Reference\nNo prior L2 knowledge for this test topic. Unit test fixture.\n",
            )
            mcp_server._write_md(
                tr / "L1" / "source_basis.md",
                {"artifact_kind": "l1_source_basis", "stage": "L1",
                 "core_sources": "paper-a", "peripheral_sources": "note-b"},
                "# Source Basis\n\n## Core Sources\npaper-a\n\n## Peripheral Sources\nnote-b\n\n## Source Roles\npaper-a is the main derivation source.\n\n## Reading Depth\nfull_read for paper-a.\n\n## Why Each Source Matters\npaper-a defines the bounded route.\n",
            )
            mcp_server._write_md(
                tr / "L1" / "convention_snapshot.md",
                {"artifact_kind": "l1_convention_snapshot", "stage": "L1",
                 "notation_choices": "Use source-a symbols.",
                 "unit_conventions": "Natural units."},
                "# Convention Snapshot\n\n## Notation Choices\nUse source-a symbols.\n\n## Unit Conventions\nNatural units.\n\n## Sign Conventions\nHamiltonian sign fixed.\n\n## Metric Or Coordinate Conventions\nEuclidean.\n\n## Categorized Assumptions\nMathematical: SU(2) symmetry. Physical: zero temperature.\n\n## Unresolved Tensions\nNone blocking.\n",
            )
            mcp_server._write_md(
                tr / "L1" / "derivation_anchor_map.md",
                {"artifact_kind": "l1_derivation_anchor_map", "stage": "L1",
                 "starting_anchors": "eq-12", "anchor_count": 1},
                "# Derivation Anchor Map\n\n## Source Anchors\neq-12\n\n## Dependency Graph\neq-12 -> eq-14.\n\n## Missing Steps\nOne omitted algebra step.\n\n## Candidate Starting Points\neq-12 to eq-14.\n",
            )
            mcp_server._write_md(
                tr / "L1" / "contradiction_register.md",
                {"artifact_kind": "l1_contradiction_register", "stage": "L1",
                 "blocking_contradictions": "none"},
                "# Contradiction Register\n\n## Unresolved Source Conflicts\nNone.\n\n## Internal Inconsistencies\nNone detected.\n\n## Regime Mismatches\nNone blocking.\n\n## Notation Collisions\nTracked and resolved.\n\n## Blocking Status\nnone\n",
            )
            mcp_server._write_md(
                tr / "L1" / "source_toc_map.md",
                {"artifact_kind": "l1_source_toc_map", "stage": "L1",
                 "sources_with_toc": "paper-a", "total_sections": 1,
                 "coverage_status": "complete"},
                "# Source TOC Map\n\n## Per-Source TOC\n\n"
                "### paper-a (TOC confidence: high)\n\n"
                "- [s1] Main Content — status: extracted  → intake: L1/intake/paper-a/s1.md\n\n"
                "## Coverage Summary\n\n## Deferred Sections\n\n## Extraction Notes\n",
            )
            intake_dir = tr / "L1" / "intake" / "paper-a"
            intake_dir.mkdir(parents=True, exist_ok=True)
            mcp_server._write_md(
                intake_dir / "s1.md",
                {"artifact_kind": "l1_section_intake", "source_id": "paper-a",
                 "section_id": "s1", "section_title": "Main Content",
                 "extraction_status": "extracted", "completeness_confidence": "high",
                 "updated_at": "2025-01-01T00:00:00Z"},
                "# Main Content\n\n## Section Summary (skim)\nContent.\n\n"
                "## Key Concepts\nConcept.\n\n## Equations Found\neq-1.\n\n"
                "## Physical Claims\nClaim.\n\n## Prerequisites\nNone.\n\n"
                "## Cross-References\nNone.\n\n## Completeness Self-Assessment\nConfidence: **high**\n",
            )

            brief = mcp_server.aitp_get_execution_brief(topics_root=tmp, topic_slug="demo-topic")
            self.assertEqual(brief["stage"], "L1")
            self.assertEqual(brief["posture"], "frame")
            self.assertEqual(brief["gate_status"], "ready")
            self.assertEqual(brief["next_allowed_transition"], "L3")
            self.assertEqual(brief["skill"], "skill-frame")


class HookOutputTests(unittest.TestCase):
    REPO_ROOT = Path(__file__).resolve().parents[1]

    def test_session_start_prints_stage_posture_and_required_artifact(self):
        with tempfile.TemporaryDirectory() as tmp:
            repo_root = Path(tmp)
            (repo_root / "topics").mkdir()
            mcp_server.aitp_bootstrap_topic(
                topics_root=str(repo_root), topic_slug="demo-topic",
                title="Demo Topic", question="What is the bounded question?",
            )
            tr = repo_root / "topics" / "demo-topic"

            # Fill L0 gate: source_registry + registered source
            mcp_server._write_md(
                tr / "L0" / "source_registry.md",
                {"artifact_kind": "l0_source_registry", "stage": "L0",
                 "source_count": 1, "search_status": "complete"},
                "# Source Registry\n\n## Search Methodology\narxiv\n\n"
                "## Source Inventory\npaper-a\n\n## Coverage Assessment\nAdequate.\n\n"
                "## Overall Verdict\nThis assessment confirms that the registered sources are sufficient for the bounded research question. Coverage spans the core derivation path and primary validation checks. No blocking gaps were identified. Source quality is adequate for current research intensity level.\n\n"
                "## Gaps And Next Sources\nNone.\n\n## Prior L2 Knowledge\nNo prior L2 knowledge for this test bootstrap topic. Unit test fixture for gate verification.\n",
            )
            mcp_server._write_md(
                tr / "L0" / "sources" / "paper-a.md",
                {"artifact_kind": "l0_source", "source_type": "paper",
                 "slug": "paper-a", "short_title": "Paper A"},
                "# Paper A\n\nA source.\n",
            )
            mcp_server.aitp_advance_to_l1(topics_root=tmp, topic_slug="demo-topic")
            completed = subprocess.run(
                [sys.executable, str(self.REPO_ROOT / "hooks" / "session_start.py")],
                cwd=repo_root, text=True, capture_output=True, check=True,
            )
            self.assertIn("stage: L1", completed.stdout)
            self.assertIn("posture: read", completed.stdout)
            self.assertIn("question_contract.md", completed.stderr)
            self.assertIn("skill-read.md", completed.stdout)

    def test_compact_prints_same_stage_posture_summary(self):
        with tempfile.TemporaryDirectory() as tmp:
            repo_root = Path(tmp)
            (repo_root / "topics").mkdir()
            mcp_server.aitp_bootstrap_topic(
                topics_root=str(repo_root), topic_slug="demo-topic",
                title="Demo Topic", question="What is the bounded question?",
            )
            tr = repo_root / "topics" / "demo-topic"

            # Fill L0 gate: source_registry + registered source
            mcp_server._write_md(
                tr / "L0" / "source_registry.md",
                {"artifact_kind": "l0_source_registry", "stage": "L0",
                 "source_count": 1, "search_status": "complete"},
                "# Source Registry\n\n## Search Methodology\narxiv\n\n"
                "## Source Inventory\npaper-a\n\n## Coverage Assessment\nAdequate.\n\n"
                "## Overall Verdict\nThis assessment confirms that the registered sources are sufficient for the bounded research question. Coverage spans the core derivation path and primary validation checks. No blocking gaps were identified. Source quality is adequate for current research intensity level.\n\n"
                "## Gaps And Next Sources\nNone.\n\n## Prior L2 Knowledge\nNo prior L2 knowledge for this test bootstrap topic. Unit test fixture for gate verification.\n",
            )
            mcp_server._write_md(
                tr / "L0" / "sources" / "paper-a.md",
                {"artifact_kind": "l0_source", "source_type": "paper",
                 "slug": "paper-a", "short_title": "Paper A"},
                "# Paper A\n\nA source.\n",
            )
            mcp_server.aitp_advance_to_l1(topics_root=tmp, topic_slug="demo-topic")
            completed = subprocess.run(
                [sys.executable, str(self.REPO_ROOT / "hooks" / "compact.py")],
                cwd=repo_root, text=True, capture_output=True, check=True,
            )
            self.assertIn("stage: L1", completed.stdout)
            self.assertIn("posture: read", completed.stdout)
            self.assertIn("skill-read.md", completed.stdout)


if __name__ == "__main__":
    unittest.main()
