"""Skill registry client.

Fetches ``registry.json`` from a GitHub repo using stdlib ``urllib``.
"""

from __future__ import annotations

import json
import os
import urllib.error
import urllib.request
from dataclasses import dataclass, field
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from icarus.skills.registry_config import RegistryEntry


class RegistryError(Exception):
    """User-facing error from registry operations."""


@dataclass
class SkillIndexEntry:
    """A single skill listing from registry.json."""

    name: str
    description: str
    version: str
    tags: list[str] = field(default_factory=list)


def _parse_github_url(url: str) -> tuple[str, str]:
    """``"github:owner/repo"`` → ``(owner, repo)``."""
    if not url.startswith("github:"):
        msg = f"Unsupported registry URL: {url!r}"
        raise RegistryError(msg)
    parts = url[len("github:"):].split("/", 1)
    if len(parts) != 2 or not parts[0] or not parts[1]:
        msg = f"Invalid registry URL: {url!r}"
        raise RegistryError(msg)
    return parts[0], parts[1]


def _auth_headers(entry: RegistryEntry) -> dict[str, str]:
    """Build Authorization header if the registry has a token configured."""
    token = os.environ.get(entry.token_env) if entry.token_env else None
    if token:
        return {"Authorization": f"token {token}"}
    return {}


def fetch_index(entry: RegistryEntry) -> list[SkillIndexEntry]:
    """Fetch registry.json from a GitHub registry.

    Returns an empty list on any error so callers don't need try/except.
    """
    owner, repo = _parse_github_url(entry.url)

    url = (
        f"https://raw.githubusercontent.com/{owner}/{repo}"
        f"/main/registry.json"
    )
    headers = _auth_headers(entry)

    req = urllib.request.Request(url, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            data = json.loads(resp.read())
    except Exception:  # noqa: BLE001
        return []

    items = data.get("skills", data) if isinstance(data, dict) else data
    if not isinstance(items, list):
        return []

    return [
        SkillIndexEntry(
            name=s["name"],
            description=s.get("description", ""),
            version=s.get("version", "0.0.0"),
            tags=s.get("tags", []),
        )
        for s in items
        if isinstance(s, dict) and s.get("name")
    ]


def fetch_skill_files(
    entry: RegistryEntry, skill_name: str
) -> dict[str, bytes]:
    """Download all files for a skill from a GitHub registry.

    Returns ``{filename: content}`` dict. Raises ``RegistryError``
    if the skill directory or SKILL.md is not found.
    """
    owner, repo = _parse_github_url(entry.url)
    headers = _auth_headers(entry)

    api_url = (
        f"https://api.github.com/repos/{owner}/{repo}"
        f"/contents/skills/{skill_name}"
    )
    req = urllib.request.Request(api_url, headers=headers)
    try:
        with urllib.request.urlopen(req, timeout=10) as resp:
            contents = json.loads(resp.read())
    except urllib.error.HTTPError as e:
        msg = f"Skill '{skill_name}' not found in '{entry.name}'"
        raise RegistryError(msg) from e
    except Exception as e:
        msg = f"Failed to fetch '{skill_name}' from '{entry.name}': {e}"
        raise RegistryError(msg) from e

    if not isinstance(contents, list):
        msg = f"Unexpected response for '{skill_name}'"
        raise RegistryError(msg)

    files: dict[str, bytes] = {}
    for item in contents:
        if item.get("type") != "file" or not item.get("download_url"):
            continue
        dl_req = urllib.request.Request(
            item["download_url"], headers=headers
        )
        with urllib.request.urlopen(dl_req, timeout=10) as resp:
            files[item["name"]] = resp.read()

    if "SKILL.md" not in files:
        msg = f"SKILL.md not found for '{skill_name}' in '{entry.name}'"
        raise RegistryError(msg)

    return files
