#!/usr/bin/env python3
"""Validate Kodi repository artifacts for ViewIT.

Usage:
  verify_repo_artifacts.py <repo_dir> [--expect-branch <branch>]
"""

from __future__ import annotations

import argparse
import hashlib
import sys
import xml.etree.ElementTree as ET
import zipfile
from pathlib import Path

PLUGIN_ID = "plugin.video.viewit"
REPO_ID = "repository.viewit"


def _find_addon(root: ET.Element, addon_id: str) -> ET.Element:
    if root.tag == "addon" and (root.attrib.get("id") or "") == addon_id:
        return root
    for addon in root.findall("addon"):
        if (addon.attrib.get("id") or "") == addon_id:
            return addon
    raise ValueError(f"addon {addon_id} not found in addons.xml")


def _read_zip_addon_version(zip_path: Path, addon_id: str) -> str:
    inner_path = f"{addon_id}/addon.xml"
    with zipfile.ZipFile(zip_path, "r") as archive:
        try:
            data = archive.read(inner_path)
        except KeyError as exc:
            raise ValueError(f"{zip_path.name}: missing {inner_path}") from exc
    root = ET.fromstring(data.decode("utf-8", errors="replace"))
    version = (root.attrib.get("version") or "").strip()
    if not version:
        raise ValueError(f"{zip_path.name}: addon.xml without version")
    return version


def _check_md5(repo_dir: Path) -> list[str]:
    errors: list[str] = []
    addons_xml = repo_dir / "addons.xml"
    md5_file = repo_dir / "addons.xml.md5"
    if not addons_xml.exists() or not md5_file.exists():
        return errors
    expected = md5_file.read_text(encoding="ascii", errors="ignore").strip().lower()
    actual = hashlib.md5(addons_xml.read_bytes()).hexdigest()
    if expected != actual:
        errors.append("addons.xml.md5 does not match addons.xml")
    return errors


def _check_repo_zip_branch(zip_path: Path, expected_branch: str) -> list[str]:
    errors: list[str] = []
    inner_path = f"{REPO_ID}/addon.xml"
    with zipfile.ZipFile(zip_path, "r") as archive:
        try:
            data = archive.read(inner_path)
        except KeyError as exc:
            raise ValueError(f"{zip_path.name}: missing {inner_path}") from exc
    root = ET.fromstring(data.decode("utf-8", errors="replace"))
    info = root.find(".//dir/info")
    if info is None or not (info.text or "").strip():
        errors.append(f"{zip_path.name}: missing repository info URL")
        return errors
    info_url = (info.text or "").strip()
    marker = f"/branch/{expected_branch}/addons.xml"
    if marker not in info_url:
        errors.append(f"{zip_path.name}: info URL does not point to branch '{expected_branch}'")
    return errors


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("repo_dir", help="Path to repository root (contains addons.xml)")
    parser.add_argument("--expect-branch", default="", help="Expected branch in repository.viewit addon.xml URL")
    args = parser.parse_args()

    repo_dir = Path(args.repo_dir).resolve()
    addons_xml = repo_dir / "addons.xml"
    if not addons_xml.exists():
        print(f"Missing: {addons_xml}", file=sys.stderr)
        return 2

    errors: list[str] = []
    try:
        root = ET.parse(addons_xml).getroot()
        plugin_node = _find_addon(root, PLUGIN_ID)
        repo_node = _find_addon(root, REPO_ID)
    except Exception as exc:
        print(f"Invalid addons.xml: {exc}", file=sys.stderr)
        return 2

    plugin_version = (plugin_node.attrib.get("version") or "").strip()
    repo_version = (repo_node.attrib.get("version") or "").strip()
    if not plugin_version:
        errors.append("plugin.video.viewit has no version in addons.xml")
    if not repo_version:
        errors.append("repository.viewit has no version in addons.xml")

    plugin_zip = repo_dir / PLUGIN_ID / f"{PLUGIN_ID}-{plugin_version}.zip"
    repo_zip = repo_dir / REPO_ID / f"{REPO_ID}-{repo_version}.zip"
    if not plugin_zip.exists():
        errors.append(f"Missing plugin zip: {plugin_zip}")
    if not repo_zip.exists():
        errors.append(f"Missing repository zip: {repo_zip}")

    if plugin_zip.exists():
        try:
            zip_version = _read_zip_addon_version(plugin_zip, PLUGIN_ID)
            if zip_version != plugin_version:
                errors.append(
                    f"{plugin_zip.name}: version mismatch (zip={zip_version}, addons.xml={plugin_version})"
                )
        except Exception as exc:
            errors.append(str(exc))

    if repo_zip.exists():
        try:
            zip_version = _read_zip_addon_version(repo_zip, REPO_ID)
            if zip_version != repo_version:
                errors.append(f"{repo_zip.name}: version mismatch (zip={zip_version}, addons.xml={repo_version})")
            if args.expect_branch:
                errors.extend(_check_repo_zip_branch(repo_zip, args.expect_branch))
        except Exception as exc:
            errors.append(str(exc))

    errors.extend(_check_md5(repo_dir))

    if errors:
        print("Repository validation failed:")
        for line in errors:
            print(f"- {line}")
        return 1

    print("Repository validation passed.")
    print(f"- plugin: {plugin_version}")
    print(f"- repository: {repo_version}")
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
