"""Skill installer — validate and hot-load a generated skill into the live registry."""

from pathlib import Path

import structlog

from fliiq.runtime.skills.base import SkillBase, _parse_fliiq_yaml, _parse_skill_md

log = structlog.get_logger()


class SkillInstallError(Exception):
    """Raised when skill validation or installation fails."""


def validate_skill_directory(skill_dir: Path) -> dict:
    """Validate a skill directory has valid SKILL.md + fliiq.yaml + main.py.

    Returns dict with name, description, credentials, and loaded SkillBase.
    Raises SkillInstallError with clear message on failure.
    """
    skill_dir = Path(skill_dir)

    # Check required files exist
    for required in ["SKILL.md", "fliiq.yaml", "main.py"]:
        if not (skill_dir / required).exists():
            raise SkillInstallError(f"Missing {required} in {skill_dir}")

    # Parse SKILL.md frontmatter
    try:
        md = _parse_skill_md(str(skill_dir))
    except Exception as e:
        raise SkillInstallError(f"Invalid SKILL.md: {e}") from e

    if not md.get("name"):
        raise SkillInstallError("SKILL.md frontmatter missing 'name'")
    if not md.get("description"):
        raise SkillInstallError("SKILL.md frontmatter missing 'description'")

    # Parse fliiq.yaml
    try:
        config = _parse_fliiq_yaml(str(skill_dir))
    except Exception as e:
        raise SkillInstallError(f"Invalid fliiq.yaml: {e}") from e

    if config.get("input_schema", {}).get("type") != "object":
        raise SkillInstallError("fliiq.yaml input_schema must have type: object")

    # Try loading as SkillBase (catches syntax errors, missing handler, etc.)
    try:
        skill = SkillBase(str(skill_dir))
    except Exception as e:
        raise SkillInstallError(f"Failed to load skill: {e}") from e

    return {
        "name": md["name"],
        "description": md["description"],
        "credentials": config.get("credentials", []),
        "test_example": config.get("test_example"),
        "skill": skill,
    }


def install_skill(skill_dir: Path, registry) -> str:
    """Validate and register a skill into the live ToolRegistry.

    Args:
        skill_dir: Path to skill directory containing SKILL.md + fliiq.yaml + main.py.
        registry: ToolRegistry instance to register the skill into.

    Returns:
        Success message string.

    Raises:
        SkillInstallError on validation failure or name collision.
    """
    info = validate_skill_directory(skill_dir)

    # Check for name collision with existing tools
    existing_names = {d["name"] for d in registry.get_tool_definitions()}
    if info["name"] in existing_names:
        raise SkillInstallError(
            f"Tool '{info['name']}' already exists. Choose a different name in SKILL.md."
        )

    # Register into live registry
    registry.register_skill(info["skill"])
    log.info("skill_installed", name=info["name"], path=str(skill_dir))

    msg = f"Skill '{info['name']}' installed successfully and available for use."
    if info["credentials"]:
        creds = ", ".join(info["credentials"])
        msg += f"\n\nRequired credentials (environment variables): {creds}"
        msg += "\nAsk the user to set these in their .env file if not already configured."

    te = info.get("test_example")
    if te:
        msg += "\n\nMANDATORY: Run tests before declaring this skill complete."
        msg += f"\n1. Run: pytest .fliiq/skills/{info['name']}/test_main.py -v"
        msg += f"\n2. Call '{info['name']}' with params: {te.get('params', {})}"
        msg += f"\n3. Verify response contains keys: {te.get('expect_keys', [])}"

    return msg
