#!/usr/bin/env python3
"""H5P Repository Mirror Manager.

Two commands:

**init** — discovers H5P repos in a GitHub org, reads ``library.json``
to extract the machineName, creates Bitbucket repos, and writes
``mirror_index.json`` keyed by machineName.

**sync** — reads ``mirror_index.json``, ensures bare clones exist,
fetches from origin, and pushes to the mirror remote.  Pure git — no
Bitbucket API calls.

Usage::

    python -m ...repository mirror init \\
        --mirror-base-url git@bitbucket.org:workspace \\
        --bb-project H5PMIR

    python -m ...repository mirror sync [machine_name]
"""
import argparse
import base64
from dataclasses import dataclass
import json
import logging
import os
from pathlib import Path
from typing import Any, Dict, Optional
from typing import ClassVar, List, Type

from byteb4rb1e.utils.argparse import CLICommand, CLI
from byteb4rb1e.utils.vcs import git
from byteb4rb1e.utils.saas import github, bitbucket

from byteb4rb1e.sphinxcontrib.h5p.utils.pkg.h5porg.repository import (
    INDEX,
    MIRROR_BASE_URL,
)

GITHUB_ORG = "h5p"
SKIP_PREFIXES = ("h5p-editor", "h5pmods")

logger = logging.getLogger(__name__)


def load_index(path: Path) -> Dict[str, Any]:
    if path.exists():
        return json.loads(path.read_text(encoding="utf-8"))
    return {}


def save_index(
    index: Dict[str, Any],
    path: Path,
) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with path.open("w", encoding="utf-8") as f:
        json.dump(index, f, indent=4, ensure_ascii=False)


def index(
    mirror_base_url: str,
    github_org: str,
    index_path: Path,
) -> int:
    """Discover H5P repos, write mirror index.

    For each repo in the GitHub org that contains a ``library.json``:

    1. Fetch ``library.json`` via the GitHub contents API and extract
       the ``machineName``.
    2. If *bb_project* is set, create the corresponding Bitbucket
       repository when it doesn't already exist.
    3. Record the entry in ``mirror_index.json`` keyed by machineName.
    """
    github_token = os.getenv("GITHUB_TOKEN")
    if not github_token:
        logger.error("GITHUB_TOKEN is required.")
        return 1

    repos = github.list_org_repos(github_org, github_token)
    logger.info(
        "Discovered %d repos in org '%s'", len(repos), github_org,
    )

    mirror_index = load_index(index_path)
    known_origins = {
        e["origin"] for e in mirror_index.values() if "origin" in e
    }
    added = 0

    for r in repos:
        name = r.get("name", "")
        slug = f"{github_org}/{name}"
        origin = f"https://github.com/{slug}.git"

        if any(name.startswith(p) for p in SKIP_PREFIXES):
            logger.debug("Skipping %s (filtered)", slug)
            continue

        if origin in known_origins:
            logger.debug("Already indexed: %s", slug)
            continue

        resp = github.fetch_file(
            github_org, name, "contents/library.json",
            github_token,
        )
        if resp.status_code != 200:
            logger.debug("Skipping %s (no library.json)", slug)
            continue

        try:
            content_b64 = resp.json().get("content", "")
            content = base64.b64decode(content_b64).decode("utf-8")
            lib = json.loads(content)
            machine_name = lib.get("machineName")
        except (json.JSONDecodeError, TypeError, KeyError,
                UnicodeDecodeError):
            logger.warning(
                "Could not parse library.json for %s", slug,
            )
            continue

        if not machine_name:
            logger.warning(
                "No machineName in library.json for %s", slug,
            )
            continue

        mirror_url = f"{mirror_base_url}/{name}.git"

        mirror_index[machine_name] = {
            "origin": origin,
            "mirror": mirror_url,
        }
        added += 1
        logger.info("Added %s (%s)", machine_name, slug)

    save_index(mirror_index, index_path)
    logger.info(
        "Init completed. "
        "Added %d, index size %d.",
        added, len(mirror_index),
    )
    return 0


@dataclass
class IndexCommand(CLICommand):
    name: str = "index"
    help: str = "discover H5P libraries and register repositories"

    def add_arguments(self, parser: argparse.ArgumentParser) -> None:
        parser.add_argument(
            "INDEX", nargs="?",
            default=str(INDEX),
            help="Output path for repository index",
        )
        parser.add_argument(
            "--mirror-base-url", default=MIRROR_BASE_URL,
            help="Base SSH URL for mirrors",
        )
        parser.add_argument(
            "--github-org", default=GITHUB_ORG,
            help="GitHub organization to discover H5P repos in",
        )

    def execute(self, args: Any) -> int:
        return index(
            mirror_base_url=args.mirror_base_url,
            github_org=args.github_org,
            index_path=Path(args.INDEX),
        )


if __name__ == "__main__":
    cli = CLI(prog="index", description=index.__doc__)
    cmd = IndexCommand()
    cmd.add_arguments(cli.parser)
    cmd.execute(cli.parser.parse_args())
