"""GitHub source resolution — fetch brand assets from remote repositories.

Supports both ``gh api`` (if authenticated) and raw HTTPS fallback.
"""

from __future__ import annotations

import json
import subprocess
import tempfile
from dataclasses import dataclass
from pathlib import Path

from tokenmoulds._compat import HAS_HTTPX, httpx
from tokenmoulds.brand.manifest import BrandManifest, load_manifest


@dataclass
class GitHubSource:
    """Coordinates for a brand manifest hosted on GitHub."""

    repository: str  # e.g. "owner/repo"
    ref: str = "main"  # branch, tag, or commit SHA
    path: str = "brand-manifest.yaml"  # path within the repo


def _fetch_raw_github(
    repository: str,
    ref: str,
    path: str,
) -> bytes:
    """Fetch a file from GitHub using raw.githubusercontent.com.

    Falls back to ``gh api`` if the HTTPS request fails (e.g. private repo).
    """
    url = f"https://raw.githubusercontent.com/{repository}/{ref}/{path}"
    if HAS_HTTPX:
        try:
            resp = httpx.get(url, timeout=30, follow_redirects=True)
            if resp.status_code == 200:
                return resp.content
        except Exception:
            pass

    # Fallback: gh api (works for private repos with auth)
    api_path = f"/repos/{repository}/contents/{path}?ref={ref}"
    result = subprocess.run(
        ["gh", "api", api_path],
        capture_output=True,
        text=True,
        check=True,
    )
    data = json.loads(result.stdout)
    # GitHub API returns base64-encoded content
    import base64

    return base64.b64decode(data["content"])


def resolve_github_source(
    source: GitHubSource,
) -> tuple[BrandManifest, Path]:
    """Fetch a brand manifest and its token source from GitHub.

    Downloads the manifest YAML and the referenced token file into a
    temporary directory, then returns the parsed manifest and the temp
    directory path.

    Args:
        source: GitHub coordinates for the manifest.

    Returns:
        Tuple of (parsed manifest, temp directory containing fetched files).
    """
    tmp_dir = Path(tempfile.mkdtemp(prefix="tokenmoulds-brand-"))

    # Fetch manifest
    manifest_bytes = _fetch_raw_github(source.repository, source.ref, source.path)
    manifest_path = tmp_dir / "brand-manifest.yaml"
    manifest_path.write_bytes(manifest_bytes)

    manifest = load_manifest(manifest_path)

    # Fetch token source file
    # Resolve token_source path relative to manifest's directory in the repo
    manifest_parent = str(Path(source.path).parent)
    if manifest_parent == ".":
        token_path = manifest.token_source
    else:
        token_path = f"{manifest_parent}/{manifest.token_source}"

    token_bytes = _fetch_raw_github(source.repository, source.ref, token_path)
    token_dest = tmp_dir / manifest.token_source
    token_dest.parent.mkdir(parents=True, exist_ok=True)
    token_dest.write_bytes(token_bytes)

    return manifest, tmp_dir


def resolve_local_source(
    manifest_path: Path,
) -> tuple[BrandManifest, Path]:
    """Load a brand manifest from the local filesystem.

    Args:
        manifest_path: Path to the local ``brand-manifest.yaml``.

    Returns:
        Tuple of (parsed manifest, directory containing the manifest).
    """
    manifest = load_manifest(manifest_path)
    return manifest, manifest_path.parent
