"""YAML loader for .prompt.yaml files.

Loads the static structure of a prompt from a YAML file.
Dynamic content (context, input_data, history) is injected at runtime.

Uses only stdlib yaml (no extra dependency).
"""

from __future__ import annotations

import yaml
from pathlib import Path
from typing import Any

from .types import (
    CoTSpec,
    CoTStrategy,
    ContextBlock,
    Example,
    InputData,
    Instruction,
    OutputSpec,
    Persona,
    PriorityEntry,
    PriorityMap,
    Prompt,
    Rule,
    RulePriority,
    Scope,
    Section,
    TruncationStrategy,
    Visibility,
    WhenScope,
)


def load(path: str | Path) -> Prompt:
    """Load a prompt from a YAML file.

    Args:
        path: Path to a .prompt.yaml file.

    Returns:
        A Prompt object with static fields populated.
        Dynamic fields (context, input_data, history) will be empty.
    """
    path = Path(path)
    with open(path) as f:
        data = yaml.safe_load(f)

    return _parse_prompt(data)


def loads(yaml_string: str) -> Prompt:
    """Load a prompt from a YAML string."""
    data = yaml.safe_load(yaml_string)
    return _parse_prompt(data)


def _parse_prompt(data: dict[str, Any]) -> Prompt:
    """Parse a prompt dict into a Prompt object."""
    kwargs: dict[str, Any] = {}

    # Persona
    if "persona" in data:
        p = data["persona"]
        kwargs["persona"] = Persona(
            role=p["role"],
            tone=p.get("tone"),
            domain=p.get("domain", []),
            anti_patterns=p.get("anti_patterns", []),
        )

    # Rules
    if "rules" in data:
        rules = []
        for r in data["rules"]:
            scope: Scope | WhenScope = Scope.ALWAYS
            if isinstance(r.get("scope"), dict) and "when" in r["scope"]:
                scope = WhenScope(condition=r["scope"]["when"])
            elif r.get("scope") == "always":
                scope = Scope.ALWAYS

            rules.append(Rule(
                constraint=r["constraint"],
                scope=scope,
                priority=RulePriority(r.get("priority", "hard")),
            ))
        kwargs["rules"] = rules

    # Instruction (required)
    if "instruction" in data:
        i = data["instruction"]
        kwargs["instruction"] = Instruction(
            task=i["task"],
            subtasks=i.get("subtasks", []),
            constraints=i.get("constraints", []),
            intent=i.get("intent"),
        )
    else:
        raise ValueError("Prompt YAML must contain an 'instruction' field.")

    # Output spec
    if "output_spec" in data:
        o = data["output_spec"]
        kwargs["output_spec"] = OutputSpec(
            format=o.get("format", "plain"),
            length=o.get("length"),
            language=o.get("language"),
            constraints=o.get("constraints", []),
        )

    # CoT
    if "cot" in data:
        c = data["cot"]
        kwargs["cot"] = CoTSpec(
            strategy=CoTStrategy(c.get("strategy", "none")),
            visibility=Visibility(c.get("visibility", "visible")),
            format=c.get("format"),
        )

    # Examples (can be in YAML)
    if "examples" in data:
        examples = []
        for ex in data["examples"]:
            examples.append(Example(
                input=ex["input"],
                output=ex["output"],
                explanation=ex.get("explanation"),
                negative=ex.get("negative", False),
                weight=ex.get("weight", 1.0),
            ))
        kwargs["examples"] = examples

    # Priority map
    if "priority" in data:
        entries = []
        for section_name, strategy_name in data["priority"].items():
            entries.append(PriorityEntry(
                section=Section(section_name),
                strategy=TruncationStrategy(strategy_name),
            ))
        kwargs["priority"] = PriorityMap(entries=entries)

    # Version
    if "version" in data:
        kwargs["version"] = str(data["version"])

    return Prompt(**kwargs)
