from __future__ import annotations

import json
import urllib.error
import urllib.request
from dataclasses import dataclass


@dataclass(frozen=True)
class UpdateInfo:
    current_version: str
    latest_version: str
    download_url: str
    notes: str
    channel: str
    update_available: bool


def _version_tuple(value: str) -> tuple[int, ...]:
    parts: list[int] = []
    for token in str(value).strip().lstrip("vV").split("."):
        digits = "".join(ch for ch in token if ch.isdigit())
        parts.append(int(digits or 0))
    while len(parts) < 3:
        parts.append(0)
    return tuple(parts)


def check_for_update(
    manifest_url: str,
    current_version: str,
    channel: str = "stable",
    timeout: float = 8.0,
) -> UpdateInfo:
    url = (manifest_url or "").strip()
    if not url:
        raise ValueError("URL маніфесту оновлень не налаштований.")
    if not url.lower().startswith(("https://", "http://")):
        raise ValueError("URL маніфесту повинен починатися з http:// або https://")

    request = urllib.request.Request(
        url,
        headers={"User-Agent": f"TiHiY-Voice-Translator/{current_version}"},
    )
    try:
        with urllib.request.urlopen(request, timeout=timeout) as response:
            payload = json.loads(response.read().decode("utf-8"))
    except urllib.error.URLError as exc:
        raise RuntimeError(f"Не вдалося завантажити маніфест: {exc}") from exc
    except json.JSONDecodeError as exc:
        raise RuntimeError("Маніфест оновлень має некоректний JSON.") from exc

    selected = payload
    if isinstance(payload, dict) and channel in payload and isinstance(payload[channel], dict):
        selected = payload[channel]
    if not isinstance(selected, dict):
        raise RuntimeError("Маніфест оновлень має некоректну структуру.")

    latest = str(selected.get("version", "")).strip()
    if not latest:
        raise RuntimeError("У маніфесті відсутнє поле version.")
    download_url = str(selected.get("download_url", selected.get("url", ""))).strip()
    notes = str(selected.get("notes", selected.get("changelog", ""))).strip()
    return UpdateInfo(
        current_version=current_version,
        latest_version=latest,
        download_url=download_url,
        notes=notes,
        channel=channel,
        update_available=_version_tuple(latest) > _version_tuple(current_version),
    )
