"""Tests for prompt_builder.loader"""

import pytest
import tempfile
from pathlib import Path

from prompt_builder import (
    load,
    loads,
    CoTStrategy,
    Visibility,
    RulePriority,
    Section,
    TruncationStrategy,
)


MINIMAL_YAML = """
instruction:
  task: "Classify this ticket."
"""

FULL_YAML = """
version: "1.2"

persona:
  role: "You are a support ticket classifier."
  tone: "terse"
  domain: ["customer support", "SaaS"]
  anti_patterns: ["don't explain unless asked"]

rules:
  - constraint: "Only use provided categories."
    scope: always
    priority: hard
  - constraint: "Flag low-confidence results."
    scope: always
    priority: soft

instruction:
  task: "Classify the support ticket."
  intent: "Route tickets to the correct team."
  subtasks:
    - "Determine urgency"
    - "Assign category"
  constraints:
    - "Use only provided context."

output_spec:
  format: json
  length: "under 200 tokens"
  constraints:
    - "Include confidence score."

cot:
  strategy: step_by_step
  visibility: extracted

examples:
  - input: "My app is crashing"
    output: '{"urgency": "high", "category": "bug"}'
    weight: 0.9
  - input: "Can I change my plan?"
    output: '{"urgency": "low", "category": "billing"}'
    explanation: "Billing questions are low urgency by default."
    weight: 0.7

priority:
  context: cut_by_relevance
  history: truncate_oldest
  examples: cut_by_weight
"""


class TestLoads:
    def test_minimal(self):
        prompt = loads(MINIMAL_YAML)
        assert prompt.instruction.task == "Classify this ticket."
        assert prompt.persona is None
        assert prompt.rules == []

    def test_full(self):
        prompt = loads(FULL_YAML)
        assert prompt.version == "1.2"
        assert prompt.persona.role == "You are a support ticket classifier."
        assert prompt.persona.tone == "terse"
        assert len(prompt.persona.domain) == 2
        assert len(prompt.rules) == 2
        assert prompt.rules[0].priority == RulePriority.HARD
        assert prompt.rules[1].priority == RulePriority.SOFT
        assert prompt.instruction.intent == "Route tickets to the correct team."
        assert len(prompt.instruction.subtasks) == 2
        assert prompt.output_spec.format == "json"
        assert prompt.cot.strategy == CoTStrategy.STEP_BY_STEP
        assert prompt.cot.visibility == Visibility.EXTRACTED
        assert len(prompt.examples) == 2
        assert prompt.examples[0].weight == 0.9

    def test_priority_map(self):
        prompt = loads(FULL_YAML)
        assert prompt.priority.get_strategy(Section.CONTEXT) == TruncationStrategy.CUT_BY_RELEVANCE
        assert prompt.priority.get_strategy(Section.HISTORY) == TruncationStrategy.TRUNCATE_OLDEST

    def test_missing_instruction_raises(self):
        with pytest.raises(ValueError, match="instruction"):
            loads("persona:\n  role: 'test'")


class TestLoadFile:
    def test_load_from_file(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".prompt.yaml", delete=False) as f:
            f.write(FULL_YAML)
            f.flush()
            prompt = load(f.name)

        assert prompt.instruction.task == "Classify the support ticket."
        assert prompt.persona is not None

    def test_load_path_object(self):
        with tempfile.NamedTemporaryFile(mode="w", suffix=".prompt.yaml", delete=False) as f:
            f.write(MINIMAL_YAML)
            f.flush()
            prompt = load(Path(f.name))

        assert prompt.instruction.task == "Classify this ticket."


class TestRoundTrip:
    def test_loaded_prompt_compiles(self):
        """A prompt loaded from YAML should compile without error."""
        from prompt_builder import compile, TokenBudget, ContextBlock

        prompt = loads(FULL_YAML)
        # Inject runtime context
        prompt.context = [
            ContextBlock(content="Customer is on Enterprise plan.", source="crm", relevance=0.8),
        ]

        result = compile(prompt, TokenBudget(context_window=8000))

        assert len(result.messages) >= 2
        assert result.total_tokens > 0

        # The system message should contain persona + rules + output spec + cot
        sys_content = result.messages[0]["content"]
        assert "classifier" in sys_content
        assert "Rules:" in sys_content
