"""Source resolution helpers for manifest execution."""

from __future__ import annotations

from importlib.resources import as_file, files
from pathlib import Path
import shutil
import subprocess
import tempfile
from typing import Final

import httpx

from revo_module_installer.config.manifest import ManifestEntry

REQUEST_TIMEOUT_SECONDS: Final[float] = 20.0


def _derive_repository_url_from_manifest(manifest_source_url: str) -> str:
    """Derive a Git repository URL from a manifest source URL.

    Args:
        manifest_source_url: URL used to load the manifest.

    Returns:
        Repository clone URL.
    """
    if "raw.githubusercontent.com/" in manifest_source_url:
        raw_path: str = manifest_source_url.split("raw.githubusercontent.com/")[1]
        path_parts: list[str] = raw_path.split("/")
        return f"https://github.com/{path_parts[0]}/{path_parts[1]}.git"

    if "github.com/" in manifest_source_url:
        path_part: str = manifest_source_url.split("github.com/")[1]
        path_segments: list[str] = path_part.split("/")
        return f"https://github.com/{path_segments[0]}/{path_segments[1]}.git"

    message: str = (
        "Unable to derive repository URL from manifest source. "
        f"Got: {manifest_source_url}"
    )
    raise ValueError(message)


def _materialize_git_artifact(
    entry: ManifestEntry,
    manifest_source_url: str,
    clone_cache: dict[tuple[str, str], Path],
) -> Path:
    """Materialize a git-based artifact and return a local path.

    Args:
        entry: Manifest entry to materialize.
        manifest_source_url: URL used to load the manifest.
        clone_cache: Cache of already cloned repositories.

    Returns:
        Path to a materialized artifact.
    """
    if manifest_source_url.startswith("package://"):
        resource_root = files("revo_module_installer.examples") / "module_1"
        temporary_directory: Path = Path(
            tempfile.mkdtemp(prefix="revo-module-package-"),
        )
        with as_file(resource_root) as resource_path:
            source_root: Path = Path(resource_path)
            copied_root: Path = temporary_directory / "module_1"
            shutil.copytree(source_root, copied_root)
        return copied_root / entry.artifact_path

    repository_url: str = _derive_repository_url_from_manifest(manifest_source_url)
    checkout_ref: str = entry.ref_value or "main"
    cache_key: tuple[str, str] = (repository_url, checkout_ref)
    if cache_key not in clone_cache:
        clone_directory: Path = Path(tempfile.mkdtemp(prefix="revo-module-clone-"))
        subprocess.run(
            ["git", "clone", "--depth", "1", repository_url, str(clone_directory)],
            check=True,
            capture_output=True,
            text=True,
        )
        if entry.ref_type in {"git_branch", "git_tag"} and entry.ref_value is not None:
            subprocess.run(
                ["git", "-C", str(clone_directory), "checkout", entry.ref_value],
                check=True,
                capture_output=True,
                text=True,
            )
        clone_cache[cache_key] = clone_directory

    return clone_cache[cache_key] / entry.artifact_path


def _materialize_url_artifact(entry: ManifestEntry) -> Path:
    """Materialize a URL artifact and return a local file path.

    Args:
        entry: Manifest entry to materialize.

    Returns:
        Path to a materialized artifact file.
    """
    artifact_url: str = entry.ref_value or entry.artifact_path
    response: httpx.Response = httpx.get(artifact_url, timeout=REQUEST_TIMEOUT_SECONDS)
    response.raise_for_status()
    temporary_directory: Path = Path(tempfile.mkdtemp(prefix="revo-module-url-"))
    artifact_path: Path = temporary_directory / "artifact"
    artifact_path.write_text(response.text, encoding="utf-8")
    return artifact_path


def resolve_entry_source_path(
    entry: ManifestEntry,
    manifest_source_url: str,
    clone_cache: dict[tuple[str, str], Path],
) -> Path:
    """Resolve the local source path for a manifest entry.

    Args:
        entry: Manifest entry to resolve.
        manifest_source_url: URL used to load the manifest.
        clone_cache: Cache of repository checkouts.

    Returns:
        Source path to apply from.
    """
    if entry.ref_type in {"git", "git_branch", "git_tag", None}:
        return _materialize_git_artifact(entry, manifest_source_url, clone_cache)
    if entry.ref_type == "url":
        return _materialize_url_artifact(entry)
    raise ValueError(f"Unsupported ref_type: {entry.ref_type}")
