from __future__ import annotations

import os
import re
import subprocess
import urllib.error
import urllib.request
from dataclasses import dataclass, field
from typing import Any, Dict, Optional
from urllib.parse import urlparse

_SOURCE_EXTS = {
    ".c",
    ".cc",
    ".cpp",
    ".cs",
    ".css",
    ".go",
    ".h",
    ".hpp",
    ".html",
    ".java",
    ".js",
    ".json",
    ".kt",
    ".m",
    ".md",
    ".php",
    ".py",
    ".rb",
    ".rs",
    ".sh",
    ".sql",
    ".swift",
    ".toml",
    ".ts",
    ".tsx",
    ".txt",
    ".xml",
    ".yaml",
    ".yml",
}

_SOURCE_BASENAMES = {
    "cargo.toml",
    "go.mod",
    "license",
    "license.md",
    "package.json",
    "pyproject.toml",
    "readme",
    "readme.md",
    "requirements.txt",
}

_LICENSE_FILES = (
    "LICENSE",
    "LICENSE.md",
    "LICENSE.txt",
    "COPYING",
    "COPYING.md",
    "COPYING.txt",
    "UNLICENSE",
)

_LICENSE_MARKERS = (
    ("mit", re.compile(r"\bmit license\b", re.IGNORECASE)),
    ("apache-2.0", re.compile(r"\bapache license\b.*\b2\.0\b", re.IGNORECASE | re.DOTALL)),
    ("bsd", re.compile(r"\bredistribution and use in source and binary forms\b", re.IGNORECASE)),
    ("mpl-2.0", re.compile(r"\bmozilla public license\b", re.IGNORECASE)),
    ("gpl", re.compile(r"\bgnu general public license\b", re.IGNORECASE)),
    ("lgpl", re.compile(r"\bgnu lesser general public license\b", re.IGNORECASE)),
    ("agpl", re.compile(r"\bgnu affero general public license\b", re.IGNORECASE)),
    ("isc", re.compile(r"\bisc license\b", re.IGNORECASE)),
    ("cc0", re.compile(r"\bcreative commons zero\b|\bcc0\b", re.IGNORECASE)),
    ("unlicense", re.compile(r"\bunlicense\b", re.IGNORECASE)),
    ("public-domain", re.compile(r"\bpublic domain\b", re.IGNORECASE)),
)

_SUPPORTED_PUBLIC_HOSTS = {"github.com", "gitlab.com", "bitbucket.org"}


@dataclass(frozen=True)
class SourceAssetPricingDecision:
    resolved_price_model: str
    reason: Optional[str] = None
    metadata: Dict[str, Any] = field(default_factory=dict)


def resolve_source_asset_pricing(
    file_path: str,
    requested_price_model: str = "auto",
) -> SourceAssetPricingDecision:
    """Resolve default pricing for source assets registered from local repos.

    Policy:
    - Only applies when the caller requested ``price_model="auto"``.
    - Only applies to source-like files within a Git repository.
    - Requires both an open-source license and a publicly reachable repo URL.
    - Falls back to ``auto`` whenever the repo cannot be verified.
    """

    if requested_price_model != "auto":
        return SourceAssetPricingDecision(resolved_price_model=requested_price_model)

    resolved_path = os.path.realpath(file_path or "")
    if not resolved_path or not _is_source_like_path(resolved_path):
        return SourceAssetPricingDecision(resolved_price_model="auto")

    repo_root = _find_git_repo_root(resolved_path)
    if not repo_root:
        return SourceAssetPricingDecision(resolved_price_model="auto")

    open_source_license = _detect_open_source_license(repo_root)
    if not open_source_license:
        return SourceAssetPricingDecision(resolved_price_model="auto")

    remote_url = _git_origin_url(repo_root)
    repo_url = _normalize_repo_web_url(remote_url)
    if not repo_url:
        return SourceAssetPricingDecision(resolved_price_model="auto")

    if not _repo_is_public(repo_url):
        return SourceAssetPricingDecision(resolved_price_model="auto")

    return SourceAssetPricingDecision(
        resolved_price_model="free",
        reason="public_git_open_source_defaults_free",
        metadata={
            "source_origin": "public_git_open_source",
            "source_repo_url": repo_url,
            "source_license": open_source_license,
            "source_pricing_policy": "public_git_open_source_defaults_free",
        },
    )


def _is_source_like_path(path: str) -> bool:
    basename = os.path.basename(path).lower()
    if basename in _SOURCE_BASENAMES:
        return True
    return os.path.splitext(basename)[1].lower() in _SOURCE_EXTS


def _find_git_repo_root(file_path: str) -> Optional[str]:
    current = os.path.dirname(file_path)
    while current and current != os.path.dirname(current):
        if os.path.isdir(os.path.join(current, ".git")) or os.path.isfile(
            os.path.join(current, ".git")
        ):
            return current
        current = os.path.dirname(current)
    return None


def _detect_open_source_license(repo_root: str) -> Optional[str]:
    for candidate in _LICENSE_FILES:
        license_path = os.path.join(repo_root, candidate)
        if not os.path.isfile(license_path):
            continue
        try:
            with open(license_path, "r", encoding="utf-8", errors="ignore") as handle:
                text = handle.read(65536)
        except OSError:
            continue
        for license_id, pattern in _LICENSE_MARKERS:
            if pattern.search(text):
                return license_id
    return None


def _git_origin_url(repo_root: str) -> Optional[str]:
    try:
        result = subprocess.run(
            ["git", "-C", repo_root, "config", "--get", "remote.origin.url"],
            capture_output=True,
            text=True,
            timeout=2,
            check=False,
        )
    except (OSError, subprocess.SubprocessError):
        return None
    if result.returncode != 0:
        return None
    url = (result.stdout or "").strip()
    return url or None


def _normalize_repo_web_url(remote_url: Optional[str]) -> Optional[str]:
    if not remote_url:
        return None

    remote_url = remote_url.strip()

    ssh_match = re.match(r"^git@([^:]+):(.+?)(?:\.git)?$", remote_url)
    if ssh_match:
        host, path = ssh_match.groups()
        return f"https://{host}/{path.rstrip('/')}"

    parsed = urlparse(remote_url)
    if parsed.scheme in {"http", "https", "ssh"} and parsed.netloc:
        path = parsed.path.rstrip("/")
        if path.endswith(".git"):
            path = path[:-4]
        host = parsed.hostname or parsed.netloc
        if path:
            return f"https://{host}{path}"
    return None


def _repo_is_public(repo_url: str) -> bool:
    parsed = urlparse(repo_url)
    host = (parsed.hostname or "").lower()
    if host not in _SUPPORTED_PUBLIC_HOSTS:
        return False
    request = urllib.request.Request(
        repo_url,
        headers={"User-Agent": "oasyce-source-policy/1.0"},
    )
    try:
        with urllib.request.urlopen(request, timeout=2) as response:
            return 200 <= getattr(response, "status", 200) < 400
    except urllib.error.HTTPError as exc:
        return 200 <= exc.code < 400
    except Exception:
        return False
