"""Tests for default skills — parsing, token budget, and configuration."""

from pathlib import Path

import pytest

from framework.skills.config import DefaultSkillConfig, SkillsConfig
from framework.skills.defaults import (
    DATA_BUFFER_KEYS,
    SKILL_REGISTRY,
    DefaultSkillManager,
    is_batch_scenario,
)
from framework.skills.parser import parse_skill_md

_DEFAULT_SKILLS_DIR = (
    Path(__file__).resolve().parent.parent / "framework" / "skills" / "_default_skills"
)


class TestDefaultSkillFiles:
    """Verify all 6 built-in SKILL.md files parse correctly."""

    def test_all_six_skills_exist(self):
        assert len(SKILL_REGISTRY) == 6

    @pytest.mark.parametrize("skill_name,dir_name", list(SKILL_REGISTRY.items()))
    def test_skill_parses(self, skill_name, dir_name):
        path = _DEFAULT_SKILLS_DIR / dir_name / "SKILL.md"
        assert path.is_file(), f"Missing SKILL.md at {path}"

        parsed = parse_skill_md(path, source_scope="framework")
        assert parsed is not None, f"Failed to parse {path}"
        assert parsed.name == skill_name
        assert parsed.description
        assert parsed.body
        assert parsed.source_scope == "framework"

    def test_combined_token_budget(self):
        """All default skill bodies combined should be under 2000 tokens (~8000 chars)."""
        total_chars = 0
        for dir_name in SKILL_REGISTRY.values():
            path = _DEFAULT_SKILLS_DIR / dir_name / "SKILL.md"
            parsed = parse_skill_md(path, source_scope="framework")
            assert parsed is not None
            total_chars += len(parsed.body)

        approx_tokens = total_chars // 4
        assert approx_tokens < 2000, (
            f"Combined default skill bodies are ~{approx_tokens} tokens "
            f"({total_chars} chars), exceeding the 2000 token budget"
        )

    def test_data_buffer_keys_all_prefixed(self):
        """All data buffer keys must start with underscore."""
        for key in DATA_BUFFER_KEYS:
            assert key.startswith("_"), f"Data buffer key missing _ prefix: {key}"


class TestDefaultSkillManager:
    def test_load_all_defaults(self):
        manager = DefaultSkillManager()
        manager.load()

        assert len(manager.active_skill_names) == 6
        for name in SKILL_REGISTRY:
            assert name in manager.active_skill_names

    def test_load_idempotent(self):
        manager = DefaultSkillManager()
        manager.load()
        first_skills = dict(manager.active_skills)
        manager.load()
        assert manager.active_skills == first_skills

    def test_build_protocols_prompt(self):
        manager = DefaultSkillManager()
        manager.load()
        prompt = manager.build_protocols_prompt()

        assert prompt.startswith("## Operational Protocols")
        # Should contain content from each active skill
        for name in SKILL_REGISTRY:
            skill = manager.active_skills[name]
            # At least some of the body should appear
            assert skill.body[:20] in prompt

    def test_protocols_prompt_empty_when_all_disabled(self):
        config = SkillsConfig(all_defaults_disabled=True)
        manager = DefaultSkillManager(config)
        manager.load()

        assert manager.build_protocols_prompt() == ""
        assert manager.active_skill_names == []

    def test_disable_single_skill(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={"hive.quality-monitor": {"enabled": False}}
        )
        manager = DefaultSkillManager(config)
        manager.load()

        assert "hive.quality-monitor" not in manager.active_skill_names
        assert len(manager.active_skill_names) == 5

    def test_disable_all_via_convention(self):
        config = SkillsConfig.from_agent_vars(default_skills={"_all": {"enabled": False}})
        manager = DefaultSkillManager(config)
        manager.load()

        assert manager.active_skill_names == []

    def test_log_active_skills(self, caplog):
        import logging

        with caplog.at_level(logging.INFO, logger="framework.skills.defaults"):
            manager = DefaultSkillManager()
            manager.load()
            manager.log_active_skills()

        assert "Default skills active:" in caplog.text

    def test_log_all_disabled(self, caplog):
        import logging

        config = SkillsConfig(all_defaults_disabled=True)
        with caplog.at_level(logging.INFO, logger="framework.skills.defaults"):
            manager = DefaultSkillManager(config)
            manager.load()
            manager.log_active_skills()

        assert "all disabled" in caplog.text


class TestSkillsConfig:
    def test_default_is_enabled(self):
        config = SkillsConfig()
        assert config.is_default_enabled("hive.note-taking") is True

    def test_explicit_disable(self):
        config = SkillsConfig(
            default_skills={"hive.note-taking": DefaultSkillConfig(enabled=False)}
        )
        assert config.is_default_enabled("hive.note-taking") is False
        assert config.is_default_enabled("hive.batch-ledger") is True

    def test_all_disabled_flag(self):
        config = SkillsConfig(all_defaults_disabled=True)
        assert config.is_default_enabled("hive.note-taking") is False
        assert config.is_default_enabled("anything") is False

    def test_from_agent_vars_basic(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={
                "hive.note-taking": {"enabled": True},
                "hive.quality-monitor": {"enabled": False},
            },
            skills=["deep-research"],
        )
        assert config.is_default_enabled("hive.note-taking") is True
        assert config.is_default_enabled("hive.quality-monitor") is False
        assert config.skills == ["deep-research"]

    def test_from_agent_vars_bool_shorthand(self):
        config = SkillsConfig.from_agent_vars(default_skills={"hive.note-taking": False})
        assert config.is_default_enabled("hive.note-taking") is False

    def test_from_agent_vars_all_disabled(self):
        config = SkillsConfig.from_agent_vars(default_skills={"_all": {"enabled": False}})
        assert config.all_defaults_disabled is True

    def test_get_default_overrides(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={
                "hive.batch-ledger": {"enabled": True, "checkpoint_every_n": 10},
            }
        )
        overrides = config.get_default_overrides("hive.batch-ledger")
        assert overrides == {"checkpoint_every_n": 10}

    def test_get_default_overrides_empty(self):
        config = SkillsConfig()
        assert config.get_default_overrides("hive.note-taking") == {}

    def test_from_agent_vars_none_inputs(self):
        config = SkillsConfig.from_agent_vars(default_skills=None, skills=None)
        assert config.skills == []
        assert config.default_skills == {}
        assert config.all_defaults_disabled is False


class TestConfigOverrideSubstitution:
    """Config overrides replace {{placeholder}} values in injected protocol text."""

    def test_quality_monitor_default_interval(self):
        manager = DefaultSkillManager()
        manager.load()
        prompt = manager.build_protocols_prompt()
        assert "Every 5 iterations" in prompt

    def test_quality_monitor_override_interval(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={"hive.quality-monitor": {"assessment_interval": 10}}
        )
        manager = DefaultSkillManager(config)
        manager.load()
        prompt = manager.build_protocols_prompt()
        assert "Every 10 iterations" in prompt
        assert "Every 5 iterations" not in prompt

    def test_error_recovery_default_retries(self):
        manager = DefaultSkillManager()
        manager.load()
        prompt = manager.build_protocols_prompt()
        assert "3+ times" in prompt

    def test_error_recovery_override_retries(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={"hive.error-recovery": {"max_retries_per_tool": 5}}
        )
        manager = DefaultSkillManager(config)
        manager.load()
        prompt = manager.build_protocols_prompt()
        assert "5+ times" in prompt
        assert "3+ times" not in prompt

    def test_context_preservation_default_threshold(self):
        manager = DefaultSkillManager()
        manager.load()
        prompt = manager.build_protocols_prompt()
        assert "45%" in prompt

    def test_context_preservation_override_threshold(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={"hive.context-preservation": {"warn_at_usage_ratio": 0.4}}
        )
        manager = DefaultSkillManager(config)
        manager.load()
        prompt = manager.build_protocols_prompt()
        assert "40%" in prompt
        assert "45%" not in prompt

    def test_no_unreplaced_placeholders_with_defaults(self):
        """All {{...}} placeholders should be replaced when using defaults."""
        manager = DefaultSkillManager()
        manager.load()
        prompt = manager.build_protocols_prompt()
        assert "{{" not in prompt


class TestBatchAutoDetection:
    """DS-12: is_batch_scenario() and batch_init_nudge property."""

    def test_detects_list_of(self):
        assert is_batch_scenario("process a list of 100 leads") is True

    def test_detects_collection_of(self):
        assert is_batch_scenario("a collection of invoices") is True

    def test_detects_items(self):
        assert is_batch_scenario("go through all items in the spreadsheet") is True

    def test_detects_for_each(self):
        assert is_batch_scenario("for each record, send an email") is True

    def test_no_match_single_task(self):
        assert is_batch_scenario("write a summary of the quarterly report") is False

    def test_batch_nudge_active_by_default(self):
        manager = DefaultSkillManager()
        manager.load()
        assert manager.batch_init_nudge is not None
        assert "_batch_ledger" in manager.batch_init_nudge

    def test_batch_nudge_none_when_skill_disabled(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={"hive.batch-ledger": {"enabled": False}}
        )
        manager = DefaultSkillManager(config)
        manager.load()
        assert manager.batch_init_nudge is None

    def test_batch_nudge_none_when_auto_detect_disabled(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={"hive.batch-ledger": {"auto_detect_batch": False}}
        )
        manager = DefaultSkillManager(config)
        manager.load()
        assert manager.batch_init_nudge is None


class TestContextWarnRatio:
    """DS-13: context_warn_ratio property."""

    def test_default_ratio(self):
        manager = DefaultSkillManager()
        manager.load()
        assert manager.context_warn_ratio == pytest.approx(0.45)

    def test_override_ratio(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={"hive.context-preservation": {"warn_at_usage_ratio": 0.3}}
        )
        manager = DefaultSkillManager(config)
        manager.load()
        assert manager.context_warn_ratio == pytest.approx(0.3)

    def test_ratio_none_when_skill_disabled(self):
        config = SkillsConfig.from_agent_vars(
            default_skills={"hive.context-preservation": {"enabled": False}}
        )
        manager = DefaultSkillManager(config)
        manager.load()
        assert manager.context_warn_ratio is None
