"""URL resolver for various reference formats."""

from __future__ import annotations

import re
from urllib.parse import urlparse

from gaol.sharing.exceptions import URLResolveError


class URLResolver:
    """Resolve various URL and reference formats to raw URLs.

    Supported formats:
    - https://... or http://... - Direct URLs (unchanged)
    - github:user/repo/path - GitHub raw URL
    - github:user/repo/path@branch - GitHub raw URL with branch
    - gist:id - GitHub gist raw URL
    - gist:id/filename - GitHub gist file raw URL

    Examples:
        >>> resolver = URLResolver()
        >>> resolver.resolve("https://example.com/profile.yaml")
        'https://example.com/profile.yaml'
        >>> resolver.resolve("github:user/repo/profiles/dev.yaml")
        'https://raw.githubusercontent.com/user/repo/main/profiles/dev.yaml'
        >>> resolver.resolve("github:user/repo/profiles/dev.yaml@develop")
        'https://raw.githubusercontent.com/user/repo/develop/profiles/dev.yaml'
    """

    # GitHub raw content base URL
    GITHUB_RAW_BASE = "https://raw.githubusercontent.com"

    # GitHub gist raw content base URL
    GIST_RAW_BASE = "https://gist.githubusercontent.com"

    # Pattern for github:user/repo/path[@branch]
    GITHUB_PATTERN = re.compile(
        r"^github:(?P<user>[^/]+)/(?P<repo>[^/@]+)/(?P<path>.+?)(?:@(?P<branch>[^@]+))?$"
    )

    # Pattern for gist:id[/filename]
    GIST_PATTERN = re.compile(
        r"^gist:(?P<id>[a-f0-9]+)(?:/(?P<filename>.+))?$"
    )

    def resolve(self, reference: str) -> str:
        """Resolve a reference to a raw URL.

        Args:
            reference: URL or reference string

        Returns:
            Resolved raw URL

        Raises:
            URLResolveError: If reference format is invalid
        """
        reference = reference.strip()

        # Direct HTTP(S) URLs - return as-is
        if reference.startswith("http://") or reference.startswith("https://"):
            return reference

        # GitHub shorthand
        if reference.startswith("github:"):
            return self._resolve_github(reference)

        # Gist shorthand
        if reference.startswith("gist:"):
            return self._resolve_gist(reference)

        # Unknown format
        raise URLResolveError(
            reference,
            "Unknown reference format. Use https://, github:user/repo/path, or gist:id",
        )

    def _resolve_github(self, reference: str) -> str:
        """Resolve github: reference to raw URL.

        Args:
            reference: github:user/repo/path[@branch] format

        Returns:
            Raw GitHub URL

        Raises:
            URLResolveError: If format is invalid
        """
        match = self.GITHUB_PATTERN.match(reference)
        if not match:
            raise URLResolveError(
                reference,
                "Invalid github: format. Use github:user/repo/path or github:user/repo/path@branch",
            )

        user = match.group("user")
        repo = match.group("repo")
        path = match.group("path")
        branch = match.group("branch") or "main"

        return f"{self.GITHUB_RAW_BASE}/{user}/{repo}/{branch}/{path}"

    def _resolve_gist(self, reference: str) -> str:
        """Resolve gist: reference to raw URL.

        Args:
            reference: gist:id[/filename] format

        Returns:
            Raw gist URL

        Raises:
            URLResolveError: If format is invalid
        """
        match = self.GIST_PATTERN.match(reference)
        if not match:
            raise URLResolveError(
                reference,
                "Invalid gist: format. Use gist:id or gist:id/filename",
            )

        gist_id = match.group("id")
        filename = match.group("filename")

        if filename:
            # Gist with specific file
            # Note: We can't get the username easily, so we use anonymous format
            # This requires the gist to be public
            return f"https://gist.github.com/{gist_id}/raw/{filename}"
        else:
            # Gist without filename - returns first file
            return f"https://gist.github.com/{gist_id}/raw"

    def is_url(self, reference: str) -> bool:
        """Check if reference is a URL or URL-like reference.

        Args:
            reference: String to check

        Returns:
            True if it's a resolvable reference
        """
        reference = reference.strip()

        # Direct URLs
        if reference.startswith("http://") or reference.startswith("https://"):
            return True

        # Shorthand formats
        if reference.startswith("github:") or reference.startswith("gist:"):
            return True

        return False

    def is_direct_url(self, reference: str) -> bool:
        """Check if reference is a direct HTTP(S) URL.

        Args:
            reference: String to check

        Returns:
            True if it's a direct URL
        """
        reference = reference.strip()
        return reference.startswith("http://") or reference.startswith("https://")

    def get_reference_type(self, reference: str) -> str:
        """Get the type of reference.

        Args:
            reference: Reference string

        Returns:
            Type: "url", "github", "gist", or "unknown"
        """
        reference = reference.strip()

        if reference.startswith("http://") or reference.startswith("https://"):
            return "url"
        if reference.startswith("github:"):
            return "github"
        if reference.startswith("gist:"):
            return "gist"
        return "unknown"

    def extract_filename(self, reference: str) -> str | None:
        """Extract the filename from a reference.

        Args:
            reference: Reference string

        Returns:
            Filename or None if not determinable
        """
        reference = reference.strip()

        # For direct URLs, parse the path
        if self.is_direct_url(reference):
            parsed = urlparse(reference)
            path = parsed.path
            if path:
                # Get the last component
                parts = path.rstrip("/").split("/")
                if parts:
                    filename = parts[-1]
                    if "." in filename:
                        return filename
            return None

        # For github: references
        if reference.startswith("github:"):
            match = self.GITHUB_PATTERN.match(reference)
            if match:
                path = match.group("path")
                parts = path.split("/")
                if parts:
                    return parts[-1]
            return None

        # For gist: references
        if reference.startswith("gist:"):
            match = self.GIST_PATTERN.match(reference)
            if match:
                return match.group("filename")
            return None

        return None


# Module-level resolver instance
_resolver: URLResolver | None = None


def get_url_resolver() -> URLResolver:
    """Get the global URL resolver instance.

    Returns:
        URLResolver instance
    """
    global _resolver
    if _resolver is None:
        _resolver = URLResolver()
    return _resolver
