#!/usr/bin/env python3
"""H5P Content Type Meta Crawler.

Reads ``mirror_index.json`` (machineName → slug/origin/mirror), walks
existing bare repo clones, extracts version data from ``library.json``
at HEAD and tagged commits, and writes a version-aware ``index.json``.

Bare clones must already exist (created by ``mirror sync``).

Usage::

    python -m byteb4rb1e.sphinxcontrib.h5p.utils.pkg.h5porg.repository index [machine_name]
"""

import argparse
import json
import logging
from pathlib import Path
from typing import Any, Dict, Optional
from dataclasses import dataclass

from byteb4rb1e.sphinxcontrib.h5p.utils.pkg.h5porg.library import (
    extract_versions,
    INDEX,
)
from byteb4rb1e.sphinxcontrib.h5p.utils.pkg.h5porg.repository import (
    CACHE_DIR,
    INDEX as REPO_INDEX,
)
from byteb4rb1e.utils.argparse import CLICommand, CLI
from byteb4rb1e.utils.vcs import git



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(
    machine_name: Optional[str] = None,
    mirror_index_path: Path = REPO_INDEX,
    cache_dir: Path = CACHE_DIR,
    index_path: Path = INDEX,
    no_fetch: bool = False,
) -> int:
    mirror_index = load_index(mirror_index_path)
    if not mirror_index:
        logger.warning(
            "Mirror index is empty or missing (%s). "
            "Run mirror init first.",
            mirror_index_path,
        )
        return 1

    if machine_name:
        if machine_name not in mirror_index:
            logger.error(
                "Machine name '%s' not found in mirror index.",
                machine_name,
            )
            return 1
        entries = {machine_name: mirror_index[machine_name]}
    else:
        entries = mirror_index

    index = {}  # type: Dict[str, Any]
    extracted = 0

    for mn, entry in entries.items():
        origin = entry.get("origin", "")
        if not origin:
            logger.warning("No origin URL for %s", mn)
            continue

        bare_path = git.bare_path_for_url(origin, cache_dir)

        if no_fetch:
            if not bare_path.exists():
                logger.warning(
                    "Bare repo missing for %s (%s). "
                    "Run mirror sync first or drop --no-fetch.",
                    mn, bare_path,
                )
                continue
        else:
            bare_path = git.ensure_bare_clone(origin, cache_dir)

        result = extract_versions(bare_path)
        if result is None:
            continue

        _, version_data = result
        index[mn] = version_data
        extracted += 1

    save_index(index, index_path)

    logger.info(
        "Meta crawler completed. "
        "Extracted %d, index size %d.",
        extracted, len(index),
    )
    return 0


@dataclass
class IndexCommand(CLICommand):
    name: str = "index"
    help: str = "extract version data from mirrors"

    def add_arguments(self, parser: argparse.ArgumentParser) -> None:
        parser.add_argument(
            "machine_name", nargs="?", default=None,
            help="Index a single library by machineName",
        )
        parser.add_argument(
            "--repository-index",
            default=REPO_INDEX,
            help="Path to mirror_index.json",
        )
        parser.add_argument(
            "--cache-dir",
            default=CACHE_DIR,
            help="Directory for bare clone cache",
        )
        parser.add_argument(
            "--output",
            default=INDEX,
            help="Output path for index.json",
        )
        parser.add_argument(
            "--no-fetch", action="store_true", default=False,
            help="Skip fetching; use existing bare clones only",
        )

    def execute(self, args: Any) -> int:
        return index(
            machine_name=args.machine_name,
            mirror_index_path=Path(args.repository_index),
            cache_dir=Path(args.cache_dir),
            index_path=Path(args.output),
            no_fetch=args.no_fetch,
        )


if __name__ == "__main__":

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