#!/usr/bin/env python3
"""Bulk git clone (full history, no LFS) of small text repos — gitlab.tend.ch.

Engagement one-off. Scope: operator-confirmed 2026-08-06 — text/small repos only
(<50MB repo+lfs per L4_repo_sizes.json), full git history (catches deleted secrets),
LFS objects SKIPPED (git-lfs not installed → clone naturally skips LFS smear; we also
set GIT_LFS_SKIP_SMUDGE=1 as belt-and-braces so .gitattributes LFS pointers don't pull).

Read-only against the target (git clone = GET only). Auth: oauth2 + OAuth token (L1 creds).
Output: repos/ under this dossier, one dir per project (namespaced path → __).
Resume-safe: existing non-empty dirs are skipped. OPLOG via stderr progress.
"""
import json
import os
import subprocess
import sys
import time
import urllib.parse
import urllib.request
import ssl
from concurrent.futures import ThreadPoolExecutor, as_completed
from pathlib import Path

HERE = Path(__file__).resolve().parent
TARGETS = json.loads((HERE / "clone_targets.json").read_text())
OUT = HERE / "repos"
OUT.mkdir(exist_ok=True)
BASE = "https://gitlab.tend.ch"
USER = "login@tend.ch"
PW = "Doornik1142"
WORKERS = 6  # gentle on the instance

CTX = ssl.create_default_context(); CTX.check_hostname = False; CTX.verify_mode = ssl.CERT_NONE

def oauth_token():
    d = urllib.parse.urlencode({"grant_type": "password", "username": USER, "password": PW}).encode()
    r = urllib.request.urlopen(urllib.request.Request(
        BASE + "/oauth/token", data=d,
        headers={"Content-Type": "application/x-www-form-urlencoded"}), context=CTX, timeout=30)
    return json.loads(r.read())["access_token"]

def clone(path, token):
    dest = OUT / path.replace("/", "__")
    if dest.exists() and any(dest.iterdir()):
        return path, "skip(exists)", 0
    # oauth2:<token>@ works for GitLab HTTP clone (token as password, any username)
    url = f"https://oauth2:{urllib.parse.quote(token, safe='')}@gitlab.tend.ch/{path}.git"
    env = dict(os.environ, GIT_LFS_SKIP_SMUDGE="1", GIT_TERMINAL_PROMPT="0")
    t0 = time.time()
    p = subprocess.run(
        ["git", "clone", "--quiet", url, str(dest)],
        env=env, capture_output=True, text=True, timeout=600)
    dt = time.time() - t0
    if p.returncode != 0:
        # sanitize stderr so the token never lands in logs
        err = p.stderr.replace(token, "***").strip()[:120]
        return path, f"FAIL:{err}", dt
    return path, "ok", dt

def main():
    token = oauth_token()
    print(f"[+] token ok; cloning {len(TARGETS)} repos → {OUT}", file=sys.stderr)
    ok = fail = skip = 0
    t0 = time.time()
    with ThreadPoolExecutor(max_workers=WORKERS) as ex:
        futs = {ex.submit(clone, p, token): p for p in TARGETS}
        for i, f in enumerate(as_completed(futs), 1):
            path, status, dt = f.result()
            if status == "ok": ok += 1
            elif status.startswith("skip"): skip += 1
            else: fail += 1
            if status != "ok" or i % 20 == 0:
                print(f"  [{i}/{len(TARGETS)}] {path}: {status} ({dt:.0f}s)", file=sys.stderr)
    print(f"[+] done ok={ok} skip={skip} fail={fail} in {time.time()-t0:.0f}s", file=sys.stderr)

if __name__ == "__main__":
    main()
