#!/usr/bin/env python3
"""Regenerate ADOPTERS.md from a GitHub code search.

Finds public repositories that build their docs with Great Docs by searching for the marker file
`great-docs.yml`, then writes a Markdown log linking each project to its documentation homepage.
"""

from __future__ import annotations

import datetime as _dt
import json
import subprocess
import sys
from pathlib import Path

# Repositories never listed: the project itself.
SELF = {"posit-dev/great-docs"}

# Output file, written at the repository root.
OUTPUT = Path(__file__).resolve().parent.parent / "ADOPTERS.md"

SEARCH_QUERY = "filename:great-docs.yml"
SEARCH_URL = "https://github.com/search?q=filename%3Agreat-docs.yml&type=code"


def _gh(*args: str) -> str:
    """Run a `gh` command and return its stdout, exiting on failure."""
    try:
        result = subprocess.run(["gh", *args], capture_output=True, text=True, check=True)
    except FileNotFoundError:
        sys.exit("error: the GitHub CLI ('gh') is not installed or not on PATH.")
    except subprocess.CalledProcessError as exc:
        sys.exit(f"error: `gh {' '.join(args)}` failed:\n{exc.stderr.strip()}")
    return result.stdout


def find_repos() -> list[str]:
    """Return sorted, de-duplicated `owner/repo` names matching the search.

    Uses the code-search REST endpoint with `--paginate` so results beyond the first 100 are
    followed via the response `Link` headers (the API caps at 1000 results total, 100 per page).
    """
    out = _gh(
        "api",
        "--paginate",
        "-X",
        "GET",
        "search/code",
        "-f",
        f"q={SEARCH_QUERY}",
        "-f",
        "per_page=100",
        "--jq",
        ".items[].repository.full_name",
    )
    return sorted({line.strip() for line in out.splitlines() if line.strip()})


def repo_meta(full_name: str) -> dict[str, str]:
    """Return `{full_name, homepage, description}` for one repository."""
    out = _gh(
        "api",
        f"repos/{full_name}",
        "--jq",
        '{full_name: .full_name, homepage: (.homepageUrl // .homepage // ""), '
        'description: (.description // "")}',
    )
    return json.loads(out)


def is_demo(full_name: str, description: str) -> bool:
    """True for repos that are great-docs demos/examples rather than adopters."""
    return "great-docs" in full_name.lower() or "great-docs" in description.lower()


def _md_row(meta: dict[str, str]) -> str:
    name = meta["full_name"].split("/")[1]
    repo = f"https://github.com/{meta['full_name']}"
    desc = meta["description"].replace("|", r"\|")
    return f"| [{name}]({meta['homepage']}) | {desc} | [repo]({repo}) |"


def _section(title: str, rows: list[dict[str, str]]) -> str:
    lines = [f"### {title}", "", "| Project | Description | Source |", "|---|---|---|"]
    lines += [_md_row(m) for m in sorted(rows, key=lambda m: m["full_name"].lower())]
    lines.append("")
    return "\n".join(lines)


def build_markdown(repos: list[dict[str, str]]) -> str:
    """Assemble the ADOPTERS.md contents from filtered repo metadata."""
    posit = [m for m in repos if m["full_name"].startswith("posit-dev/")]
    community = [m for m in repos if not m["full_name"].startswith("posit-dev/")]
    today = _dt.date.today().isoformat()
    total = len(repos)

    header = (
        "# Sites built with Great Docs\n\n"
        "A running log of public repositories that use "
        "[Great Docs](https://github.com/posit-dev/great-docs) to build their "
        "documentation sites. Each entry links to the project's documentation "
        "homepage.\n\n"
        "> [!NOTE]\n"
        f"> This list is generated by [`scripts/update_adopters.py`]"
        "(scripts/update_adopters.py) from a GitHub code search for the marker "
        f"file `great-docs.yml` ([`{SEARCH_QUERY}`]({SEARCH_URL})). Repositories "
        "without a published homepage, and great-docs demos/examples, are "
        "omitted.\n"
        f"> Last updated: {today} ({total} repositories).\n\n"
    )
    return header + "\n".join(
        [_section("Posit projects", posit), _section("Community projects", community)]
    )


def main() -> None:
    names = [n for n in find_repos() if n not in SELF]
    print(f"Found {len(names)} candidate repositories; fetching metadata...")

    kept: list[dict[str, str]] = []
    for name in names:
        meta = repo_meta(name)
        # Keep only live sites that are genuine adopters: a published homepage, and not a
        # great-docs demo/example.
        if not meta["homepage"]:
            continue
        if is_demo(meta["full_name"], meta["description"]):
            continue
        kept.append(meta)

    OUTPUT.write_text(build_markdown(kept))
    print(f"Wrote {OUTPUT} with {len(kept)} adopters.")


if __name__ == "__main__":
    main()
