#!/bin/bash
#
# submodule-detector.sh
# Given a git repository (worktree or remote), emit JSON of suggested
# dev-context repos based on .gitmodules + sibling-checkout heuristic.
#
# Usage:
#   ./submodule-detector.sh <repo-path>                # local checkout
#   ./submodule-detector.sh --remote <owner>/<repo>    # via gh
#
# Optional env (when set, enriches each submodule with a canPush flag by
# cross-referencing the repo-cache.sh output for the active account, plus the
# editableRelatedRepos override list under prefs.projects[<key>]):
#   ACCOUNT_GH_OWNER        -  owner scope for the github cache file
#   ACCOUNT_BB_PROJECT_KEY  -  project/workspace key for the bitbucket cache
#   PROJECT_PREF_KEY        -  top-level key in prefs.projects (e.g. cwd repo name)
#
# Output:
#   [
#     {"name":"<submodule-name>","url":"https://...","provider":"github",
#      "source":"submodule","canPush":true|false,
#      "reason":"cache|owner-match|prefs-override|unknown"},
#     ...
#   ]
#

set -euo pipefail

MODE="local"
TARGET="${1:-}"
if [ "$TARGET" = "--remote" ]; then
  MODE="remote"
  TARGET="${2:-}"
fi
[ -z "$TARGET" ] && { echo "[]"; exit 0; }

emit_json() {
  GH_CACHE="$HOME/.claude/cache/repos/github-${ACCOUNT_GH_OWNER:-_none_}.json" \
  BB_CACHE="$HOME/.claude/cache/repos/bitbucket-${ACCOUNT_BB_PROJECT_KEY:-_none_}.json" \
  PREFS_FILE="$HOME/.claude/multi-agent-preferences.json" \
  PROJECT_PREF_KEY="${PROJECT_PREF_KEY:-}" \
  ACCOUNT_GH_OWNER="${ACCOUNT_GH_OWNER:-}" \
  python3 -c '
import sys, json, re, os

def load_json(path):
    try:
        with open(path) as f:
            return json.load(f)
    except Exception:
        return None

gh_cache = load_json(os.environ.get("GH_CACHE","")) or []
bb_cache = load_json(os.environ.get("BB_CACHE","")) or []
prefs = load_json(os.environ.get("PREFS_FILE","")) or {}

def index_cache(entries):
    idx = {}
    for r in entries:
        if not isinstance(r, dict):
            continue
        full = (r.get("fullName") or "").lower()
        if full:
            idx[full] = r
        for k in ("cloneUrl", "httpsCloneUrl"):
            u = (r.get(k) or "")
            if u:
                idx[u.lower()] = r
    return idx

cache_idx = {}
cache_idx.update(index_cache(gh_cache))
cache_idx.update(index_cache(bb_cache))

project_key = os.environ.get("PROJECT_PREF_KEY","")
project_pref = ((prefs.get("projects") or {}).get(project_key) or {}) if project_key else {}
overrides = project_pref.get("editableRelatedRepos") or []
override_set = { (o or "").lower() for o in overrides }

account_owner = (os.environ.get("ACCOUNT_GH_OWNER","") or "").lower()

def parse_owner_repo(url):
    s = url.strip()
    if s.startswith("git@"):
        _, _, tail = s.partition(":")
    else:
        m = re.match(r"^[a-z]+://[^/]+/(.+)$", s)
        tail = m.group(1) if m else s
    tail = tail.rstrip("/")
    if tail.endswith(".git"):
        tail = tail[:-4]
    parts = tail.split("/")
    if len(parts) >= 2:
        return parts[-2], parts[-1]
    return None, parts[-1] if parts else None

def resolve_can_push(item):
    url = item.get("url","")
    name = item.get("name","") or ""
    owner, repo = parse_owner_repo(url)
    full = f"{owner}/{repo}".lower() if owner and repo else ""

    if full and full in override_set:
        return True, "prefs-override"
    if name and name.lower() in override_set:
        return True, "prefs-override"

    for key in (full, url.lower()):
        if key and key in cache_idx:
            hit = cache_idx[key]
            return bool(hit.get("canPush", False)), "cache"

    if account_owner and owner and owner.lower() == account_owner:
        return True, "owner-match"

    return False, "unknown"

text = sys.stdin.read()
items = []
current = {}
for line in text.splitlines():
    line = line.strip()
    if line.startswith("[submodule"):
        if current:
            items.append(current)
        current = {"source": "submodule"}
        m = re.match(r"\[submodule \"(.+)\"\]", line)
        if m:
            current["name"] = m.group(1)
    elif "=" in line and current:
        k, _, v = line.partition("=")
        k = k.strip(); v = v.strip()
        if k == "url":
            current["url"] = v
            if "github.com" in v:
                current["provider"] = "github"
            elif "bitbucket" in v:
                current["provider"] = "bitbucket"
            else:
                current["provider"] = "git"
            tail = v.rstrip("/").split("/")[-1].replace(".git","")
            current.setdefault("name", tail)
        elif k == "path":
            current["path"] = v
if current:
    items.append(current)

for it in items:
    can, reason = resolve_can_push(it)
    it["canPush"] = can
    it["reason"] = reason

print(json.dumps(items))
'
}

case "$MODE" in
  local)
    if [ ! -f "$TARGET/.gitmodules" ]; then
      echo "[]"
      exit 0
    fi
    cat "$TARGET/.gitmodules" | emit_json
    ;;

  remote)
    if ! command -v gh >/dev/null 2>&1; then
      echo "[]"
      exit 0
    fi
    # emit_json already prints a valid "[]" on empty/unparseable stdin (a
    # failed `gh api` call, under pipefail, still runs it through the rest of
    # this pipe with empty input) - the `|| echo "[]"` fallback used to ALSO
    # fire on the pipeline's own (gh api's) non-zero exit, printing a SECOND
    # "[]" line and leaving invalid double-JSON on stdout. `|| true` keeps
    # the local-mode contract (unreachable submodule data -> empty list,
    # exit 0, never a hard error) without re-printing anything.
    gh api "repos/$TARGET/contents/.gitmodules" \
        --jq '.content' 2>/dev/null \
      | tr -d '\n' \
      | base64 --decode 2>/dev/null \
      | emit_json || true
    ;;
esac
