"""
SkillBase -- loads a skill from a directory containing SKILL.md + fliiq.yaml.

Unified skill format:
  SKILL.md   — YAML frontmatter (name, description) + markdown body (instructions)
  fliiq.yaml — input_schema (JSON Schema), output_schema (JSON Schema), credentials
  main.py    — async handler function
"""

import importlib.util
import inspect
import os
from contextlib import contextmanager
from typing import Any, Optional

import yaml


@contextmanager
def temp_env_context(credentials: Optional[dict[str, str]] = None):
    """Temporarily inject environment variables for credential passthrough."""
    if not credentials:
        yield
        return

    original_values = {}
    for key, value in credentials.items():
        original_values[key] = os.environ.get(key)
        os.environ[key] = value

    try:
        yield
    finally:
        for key, original_value in original_values.items():
            if original_value is None:
                os.environ.pop(key, None)
            else:
                os.environ[key] = original_value


def _parse_skill_md(skill_dir: str) -> dict:
    """Parse SKILL.md — extract YAML frontmatter and markdown body."""
    path = os.path.join(skill_dir, "SKILL.md")
    with open(path) as f:
        content = f.read()

    # Parse YAML frontmatter between --- delimiters
    if not content.startswith("---"):
        raise ValueError(f"SKILL.md missing YAML frontmatter: {path}")

    end = content.index("---", 3)
    frontmatter = yaml.safe_load(content[3:end])
    body = content[end + 3:].strip()

    return {
        "name": frontmatter["name"],
        "description": frontmatter["description"],
        "body": body,
    }


def _parse_fliiq_yaml(skill_dir: str) -> dict:
    """Parse fliiq.yaml — input_schema, output_schema, credentials."""
    path = os.path.join(skill_dir, "fliiq.yaml")
    if not os.path.exists(path):
        return {"input_schema": {"type": "object", "properties": {}}, "output_schema": {}}

    with open(path) as f:
        data = yaml.safe_load(f) or {}

    return {
        "input_schema": data.get("input_schema", {"type": "object", "properties": {}}),
        "output_schema": data.get("output_schema", {}),
        "credentials": data.get("credentials", []),
        "test_example": data.get("test_example"),
    }


class SkillBase:
    """Load and execute a skill from a directory with SKILL.md + fliiq.yaml."""

    def __init__(self, skill_dir: str):
        self._skill_dir = os.path.abspath(skill_dir)

        # Parse SKILL.md frontmatter
        md = _parse_skill_md(self._skill_dir)
        self._name = md["name"]
        self._description = md["description"]
        self._body = md["body"]

        # Parse fliiq.yaml
        config = _parse_fliiq_yaml(self._skill_dir)
        self._input_schema = config["input_schema"]
        self._output_schema = config.get("output_schema", {})
        self._credentials = config.get("credentials", [])
        self._test_example = config.get("test_example")

        # Load handler function from main.py
        file_path = os.path.join(self._skill_dir, "main.py")
        unique_name = f"_fliiq_skill_{self._name}_main"
        spec = importlib.util.spec_from_file_location(unique_name, file_path)
        skill_mod = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(skill_mod)
        self._handler = getattr(skill_mod, "handler")

    @property
    def name(self) -> str:
        return self._name

    @property
    def description(self) -> str:
        return self._description

    @property
    def test_example(self) -> dict | None:
        return self._test_example

    def schema(self) -> dict:
        """Tool definition for LLM function calling."""
        return {
            "name": self._name,
            "description": self._description,
            "parameters": self._input_schema,
            "output_schema": self._output_schema,
        }

    async def execute(self, params: dict[str, Any], credentials: Optional[dict[str, str]] = None) -> Any:
        """Run the skill handler with optional credential injection."""
        with temp_env_context(credentials):
            result = self._handler(params)
            if inspect.iscoroutine(result):
                result = await result
            return result
