"""Single source of truth for the application version string.

Resolution order mirrors the Dockerfile build chain so the dev checkout and
the runtime container agree on the value:

    REMNAWAVE_MINISHOP_VERSION env > .build-version file > live ``git describe``
    > ``dev+unknown``

The same value powers the admin sidebar (web process) and the anonymous
telemetry beacon (worker process), so "active installs" and version
breakdowns line up across both.
"""

from __future__ import annotations

import os
import re
import subprocess
from pathlib import Path
from typing import Optional

# ``/app`` in the container (parent of ``/app/backend``); repo root in dev.
# Matches where the Dockerfile drops .build-version / .build-tag / .build-commit.
APP_ROOT = Path(__file__).resolve().parents[3]

_APP_VERSION_CACHE: Optional[str] = None


def _run_git_command(*args: str) -> str:
    try:
        result = subprocess.run(
            ["git", *args],
            cwd=APP_ROOT,
            check=True,
            capture_output=True,
            text=True,
            timeout=1.5,
        )
    except (OSError, subprocess.SubprocessError):
        return ""
    return result.stdout.strip()


def _normalize_version_branch(raw_branch: str) -> str:
    branch = str(raw_branch or "").strip()
    for prefix in ("refs/heads/", "refs/remotes/origin/", "origin/"):
        if branch.startswith(prefix):
            branch = branch[len(prefix) :]
            break
    if branch in ("", "HEAD"):
        return ""
    return re.sub(r"[^A-Za-z0-9._-]+", "-", branch).strip("-")[:48]


def _resolve_version_branch() -> str:
    for env_name in (
        "REMNAWAVE_MINISHOP_BRANCH",
        "GIT_BRANCH",
        "BRANCH_NAME",
        "GITHUB_REF_NAME",
        "CI_COMMIT_REF_NAME",
    ):
        branch = _normalize_version_branch(os.getenv(env_name, ""))
        if branch:
            return branch
    return _normalize_version_branch(
        _run_git_command("branch", "--show-current")
        or _run_git_command("symbolic-ref", "--quiet", "--short", "HEAD")
    )


def _format_app_version(tag: str, sha: str, branch: str) -> str:
    branch_suffix = "" if not branch or branch == "main" else f"-{branch}"
    if tag and sha:
        return f"{tag}{branch_suffix}+g{sha}"
    if sha:
        return f"dev{branch_suffix}+g{sha}"
    if tag:
        return f"{tag}{branch_suffix}"
    return f"dev{branch_suffix}+unknown"


def _read_build_file(name: str) -> str:
    try:
        return (APP_ROOT / name).read_text(encoding="utf-8").strip()
    except OSError:
        return ""


def resolve_app_version() -> str:
    """Full version string (cached), e.g. ``v3.4.6+gabc1234``."""
    global _APP_VERSION_CACHE
    if _APP_VERSION_CACHE:
        return _APP_VERSION_CACHE

    env_version = os.getenv("REMNAWAVE_MINISHOP_VERSION", "").strip()
    if env_version:
        _APP_VERSION_CACHE = env_version
        return env_version

    build_version = _read_build_file(".build-version")
    if build_version:
        _APP_VERSION_CACHE = build_version
        return build_version

    tag = _run_git_command("describe", "--tags", "--abbrev=0")
    sha = _run_git_command("rev-parse", "--short", "HEAD")
    branch = _resolve_version_branch()
    version = _format_app_version(tag, sha, branch)
    _APP_VERSION_CACHE = version
    return version


def resolve_app_version_tag() -> str:
    """Clean release tag for low-cardinality breakdowns, e.g. ``v3.4.6``.

    Prefers the build-time ``.build-tag`` artifact, then a live ``git
    describe``; falls back to the full version string when no tag is known.
    """
    build_tag = _read_build_file(".build-tag")
    if build_tag and build_tag != "unknown":
        return build_tag
    tag = _run_git_command("describe", "--tags", "--abbrev=0")
    if tag:
        return tag
    return resolve_app_version()
