#!/usr/bin/env python3
"""
/usr/local/bin/git-deploy-all.py

Iterates /etc/freshvibe/sites.json and deploys each site.

Each site has:
  - path: the working tree on disk
  - vendors: list of {name, repo, ref}

For each vendor:
  1. cd to the site path
  2. ensure remote origin points to the vendor repo
  3. fetch the ref
  4. if HEAD != FETCH_HEAD, reset to FETCH_HEAD
  5. log it

Cron: */2 * * * * root /usr/local/bin/git-deploy-all.py >> /var/log/freshvibe-deploy.log 2>&1

To add a new site: edit /etc/freshvibe/sites.json
To add a new vendor to a site: add an entry to the vendors array
"""
import json
import subprocess
import sys
from datetime import datetime, timezone
from pathlib import Path

REGISTRY = Path("/etc/freshvibe/sites.json")
LOG = []

def log(msg):
    ts = datetime.now(timezone.utc).isoformat()
    line = f"[{ts}] {msg}"
    print(line)
    LOG.append(line)

def run(cmd, cwd=None, check=True):
    r = subprocess.run(cmd, cwd=cwd, capture_output=True, text=True)
    if check and r.returncode != 0:
        raise RuntimeError(f"cmd failed: {cmd}\n{r.stderr}")
    return r

def deploy_site(name, cfg):
    path = cfg.get("path")
    if not path or not Path(path).is_dir():
        log(f"{name}: SKIP (path not found: {path})")
        return "skipped"
    
    if not Path(path, ".git").is_dir():
        log(f"{name}: SKIP (not a git repo: {path})")
        return "skipped"
    
    status = "no-change"
    for vendor in cfg.get("vendors", []):
        vname = vendor.get("name", "unknown")
        repo = vendor.get("repo", "")
        ref = vendor.get("ref", "main")
        
        if not repo:
            log(f"{name}/{vname}: SKIP (no repo)")
            continue
        
        try:
            # Ensure remote
            current = run(["git", "remote", "get-url", "origin"], cwd=path, check=False).stdout.strip()
            if current != repo:
                run(["git", "remote", "set-url", "origin", repo], cwd=path, check=False)
            
            # Fetch
            fetch = run(["git", "fetch", repo, ref], cwd=path, check=False)
            if fetch.returncode != 0:
                log(f"{name}/{vname}: ERROR fetching {repo}@{ref} ({fetch.stderr.strip()[:100]})")
                status = "error"
                continue
            
            # Compare
            before = run(["git", "rev-parse", "HEAD"], cwd=path, check=False).stdout.strip() or "none"
            after = run(["git", "rev-parse", "FETCH_HEAD"], cwd=path, check=False).stdout.strip() or "none"
            
            if before != after:
                run(["git", "reset", "--hard", "FETCH_HEAD"], cwd=path)
                log(f"{name}/{vname}: deployed {before[:8]} -> {after[:8]} (from {repo}@{ref})")
                status = "deployed"
            else:
                # only log if no-change at site level (silent no-op)
                pass
        except Exception as e:
            log(f"{name}/{vname}: EXCEPTION {e}")
            status = "error"
    
    return status

def main():
    if not REGISTRY.is_file():
        log(f"ERROR: {REGISTRY} not found")
        sys.exit(1)
    
    sites = json.loads(REGISTRY.read_text())
    counts = {"deployed": 0, "skipped": 0, "no-change": 0, "error": 0}
    
    for name, cfg in sites.items():
        status = deploy_site(name, cfg)
        if status in counts:
            counts[status] += 1
    
    log(f"summary: {len(sites)} sites, " + ", ".join(f"{k}={v}" for k, v in counts.items()))

if __name__ == "__main__":
    main()
