from __future__ import annotations

from pathlib import Path

import yaml


class LlmPromptCatalogLoader:
    def load(self, path: Path) -> dict:
        payload = yaml.safe_load(path.read_text(encoding="utf-8"))
        if not isinstance(payload, dict):
            raise ValueError(f"Invalid prompt catalog: expected mapping, got {type(payload).__name__}")
        default = self._normalize_item("default", payload.get("default"))
        intents_raw = payload.get("intents", {})
        if not isinstance(intents_raw, dict):
            raise ValueError("Invalid prompt catalog: `intents` must be a mapping")
        intents: dict[str, dict] = {}
        for intent, item in intents_raw.items():
            intent_key = str(intent).strip()
            if not intent_key:
                continue
            intents[intent_key] = self._normalize_intent(intent_key, item)
        return {"default": default, "intents": intents}

    def select_for_intent(self, catalog: dict, intent: str) -> dict:
        return self.select(catalog, intent=intent)

    def select(self, catalog: dict, *, intent: str, sub_intent: str | None = None) -> dict:
        intents = dict(catalog.get("intents") or {})
        intent_entry = dict(intents.get(intent) or {})
        sub_intents = dict(intent_entry.get("sub_intents") or {})
        if sub_intent and sub_intent in sub_intents:
            return dict(sub_intents[sub_intent])
        if intent_entry:
            prompt = dict(intent_entry.get("prompt") or {})
            if prompt:
                return prompt
        return dict(catalog.get("default") or {})

    def _normalize_intent(self, key: str, raw: object) -> dict:
        if not isinstance(raw, dict):
            raise ValueError(f"Invalid prompt catalog item `{key}`: expected object")
        prompt = self._normalize_item(key, raw)
        sub_intents_raw = raw.get("sub_intents", {})
        if sub_intents_raw is None:
            sub_intents_raw = {}
        if not isinstance(sub_intents_raw, dict):
            raise ValueError(f"Invalid prompt catalog item `{key}`: `sub_intents` must be a mapping")
        sub_intents: dict[str, dict] = {}
        for sub_intent, item in sub_intents_raw.items():
            sub_key = str(sub_intent).strip()
            if not sub_key:
                continue
            sub_intents[sub_key] = self._normalize_item(f"{key}.{sub_key}", item)
        return {"prompt": prompt, "sub_intents": sub_intents}

    def _normalize_item(self, key: str, raw: object) -> dict:
        if not isinstance(raw, dict):
            raise ValueError(f"Invalid prompt catalog item `{key}`: expected object")
        system_prompt = str(raw.get("system_prompt", "")).strip()
        task_prompt = str(raw.get("task_prompt", "")).strip()
        template_id = str(raw.get("template_id", "")).strip() or f"intent_{key.lower()}_v1"
        if not system_prompt:
            raise ValueError(f"Invalid prompt catalog item `{key}`: `system_prompt` is required")
        if not task_prompt:
            raise ValueError(f"Invalid prompt catalog item `{key}`: `task_prompt` is required")
        return {
            "template_id": template_id,
            "system_prompt": system_prompt,
            "task_prompt": task_prompt,
        }
