"""F11 Bootstrap 拼装单元测试

按 test-plan-v2.6.md §F11：
- T1: 各 role 拼装（P0）
- T2: token 估算（P0）
- T3: 缺失组件降级（P1）
- T4: 模板变量替换（P1）
"""

import pytest
from pathlib import Path

from src.blackboard.models import Task
from src.daemon.bootstrap import BootstrapBuilder, estimate_tokens


@pytest.fixture
def builder():
    return BootstrapBuilder(max_tokens=4096)


@pytest.fixture
def builder_with_templates(tmp_path):
    template_dir = tmp_path / "templates"
    template_dir.mkdir()
    (template_dir / "executor.md").write_text("# Executor Role\nYou execute tasks.")
    (template_dir / "reviewer.md").write_text("# Reviewer Role\nYou review code.")
    (template_dir / "planner.md").write_text("# Planner Role\nYou plan tasks.")
    return BootstrapBuilder(template_dir=template_dir, max_tokens=4096)


# ---------------------------------------------------------------------------
# T1: 各 role 拼装
# ---------------------------------------------------------------------------

class TestRoleBootstrap:
    def test_executor_bootstrap(self, builder):
        b = builder.build(
            role="executor",
            task_context={"task_id": "t1", "title": "Write tests"},
        )
        assert "Write tests" in b
        assert "t1" in b

    def test_reviewer_bootstrap(self, builder):
        b = builder.build(
            role="reviewer",
            task_context={"task_id": "t2", "title": "Review PR"},
        )
        assert "Review PR" in b

    def test_planner_bootstrap(self, builder):
        b = builder.build(
            role="planner",
            task_context={"task_id": "t3", "title": "Plan sprint"},
        )
        assert "Plan sprint" in b

    def test_executor_with_guardrail(self, builder):
        b = builder.build(
            role="executor",
            guardrail_rules="## Guardrail\nNo dangerous ops",
        )
        assert "Guardrail" in b

    def test_reviewer_no_guardrail(self, builder):
        b = builder.build(
            role="reviewer",
            guardrail_rules="## Guardrail\nNo dangerous ops",
        )
        assert "Guardrail" not in b  # reviewer 不注入 guardrail

    def test_executor_with_review_protocol(self, builder):
        b = builder.build(
            role="executor",
            review_protocols="## Review Protocol\nCheck tests",
        )
        assert "Review Protocol" in b

    def test_reviewer_with_review_protocol(self, builder):
        b = builder.build(
            role="reviewer",
            review_protocols="## Review Protocol\nCheck quality",
        )
        assert "Review Protocol" in b

    def test_with_template(self, builder_with_templates):
        b = builder_with_templates.build(role="executor")
        assert "Executor Role" in b
        assert "You execute tasks" in b


# ---------------------------------------------------------------------------
# T2: token 估算
# ---------------------------------------------------------------------------

class TestTokenEstimation:
    def test_estimate_tokens_basic(self):
        assert estimate_tokens("hello") > 0

    def test_estimate_tokens_long_text(self):
        text = "a" * 1000
        tokens = estimate_tokens(text)
        assert 200 < tokens < 400  # ~333

    def test_bootstrap_under_limit(self, builder):
        b = builder.build(
            role="executor",
            task_context={"title": "Short task"},
        )
        assert estimate_tokens(b) <= 4096

    def test_bootstrap_over_limit_truncates(self):
        builder = BootstrapBuilder(max_tokens=10)
        b = builder.build(
            role="executor",
            task_context={"title": "A" * 10000},
        )
        assert estimate_tokens(b) <= 20  # 接近限制
        assert "truncated" in b


# ---------------------------------------------------------------------------
# T3: 缺失组件降级
# ---------------------------------------------------------------------------

class TestGracefulDegradation:
    def test_no_task_context(self, builder):
        b = builder.build(role="executor")
        assert b  # 不为空

    def test_no_project_context(self, builder):
        b = builder.build(
            role="executor",
            task_context={"title": "Task"},
        )
        assert "Task" in b

    def test_no_template(self, builder):
        b = builder.build(role="executor")
        assert b  # 没有 template 也不崩溃

    def test_empty_experiences(self, builder):
        b = builder.build(role="executor", experiences=[])
        assert b

    def test_template_dir_not_exists(self, tmp_path):
        builder = BootstrapBuilder(template_dir=tmp_path / "nonexistent")
        b = builder.build(role="executor")
        assert b  # 不崩溃


# ---------------------------------------------------------------------------
# T4: 便捷方法 + 项目上下文
# ---------------------------------------------------------------------------

class TestBuildForTask:
    def test_build_for_task_object(self, builder):
        task = Task(
            id="t1", title="Build Feature", status="pending",
            assigned_by="daemon", task_type="coding",
            description="Implement X with tests",
            must_haves="Unit tests, Documentation",
            risk_level="high",
        )
        b = builder.build_for_task(task, role="executor")
        assert "Build Feature" in b
        assert "Implement X" in b
        assert "high" in b

    def test_build_for_task_with_project(self, builder):
        task = Task(id="t1", title="T", status="pending", assigned_by="d")
        b = builder.build_for_task(
            task, role="executor",
            project_config={"name": "My Project", "agents": ["a1", "a2"]},
        )
        assert "My Project" in b
        assert "a1" in b

    def test_with_experiences(self, builder):
        task = Task(id="t1", title="T", status="pending", assigned_by="d")
        experiences = [
            {"category": "pitfall", "summary": "Always test edge cases"},
            {"category": "best_practice", "summary": "Use type hints"},
        ]
        b = builder.build_for_task(task, role="executor", experiences=experiences)
        assert "pitfall" in b
        assert "Always test edge cases" in b

    def test_with_skills(self, builder):
        skills = [
            {"name": "code-review", "description": "Review code quality"},
        ]
        b = builder.build(
            role="executor",
            skill_descriptions=skills,
        )
        assert "code-review" in b
        assert "Review code quality" in b

    def test_with_depends_on_outputs(self, builder):
        b = builder.build(
            role="executor",
            task_context={
                "title": "T",
                "depends_on_outputs": [
                    {"task_id": "t0", "summary": "Data downloaded"},
                ],
            },
        )
        assert "前序产出" in b
        assert "Data downloaded" in b
