#!/usr/bin/env bash
# Grok web management · organize links · check logins readiness · reboot helpers
# Secrets never printed. Waterfox holds browser sessions; desk holds pass files.
#
#   web-manage status|check|links|open GROUP|boot|login-report|html
#
set -euo pipefail
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
MAG="${BGRTX_MAG:-$HOME/Projects/biggrinrtx-magazine}"
CFG="${XDG_CONFIG_HOME:-$HOME/.config}/biggrinrtx"
LINKS="${WEB_LINKS:-$ROOT/json/web-links.json}"
OUT="$ROOT/out"
REPORT="$OUT/web_manage_latest.json"
UA="BGRTX-WebManage/1.0"

mkdir -p "$OUT" "$CFG"

cmd="${1:-status}"
shift || true

http_code() {
  curl -sS -o /dev/null -w '%{http_code}' --connect-timeout 6 --max-time 15 \
    -A "$UA" -H 'Cache-Control: no-cache' "$1" 2>/dev/null || echo 000
}

file_ok() {
  local p="$1"
  p="${p#file:}"
  # expand short forms
  case "$p" in
    Projects/*) p="$HOME/$p" ;;
    config/*) p="$CFG/${p#config/}" ;;
  esac
  [[ -e "$p" ]] && echo 1 || echo 0
}

login_ready() {
  # returns JSON fragment: ready bool + how
  local kind="$1"
  case "$kind" in
    none) echo '{"ready":true,"via":"none"}' ;;
    waterfox)
      if [[ -f "$HOME/.waterfox/default-release/logins.json" ]] || \
         [[ -f "$HOME/.waterfox/nnykdiz5.default-release-1/logins.json" ]]; then
        echo '{"ready":true,"via":"waterfox-logins-present"}'
      else
        echo '{"ready":false,"via":"waterfox-missing"}'
      fi
      ;;
    oauth-file)
      if [[ -f "$MAG/.wp-oauth.json" ]] || [[ -f "$CFG/wp-oauth.json" ]]; then
        echo '{"ready":true,"via":"wp-oauth-file"}'
      else
        echo '{"ready":false,"via":"no-oauth"}'
      fi
      ;;
    titan-pass|app-pass)
      local f
      if [[ "$kind" == titan-pass ]]; then f="$CFG/titan.pass"
      else f="$CFG/bsky.app.pass"; fi
      if [[ -f "$f" ]] && [[ -s "$f" ]]; then
        echo "{\"ready\":true,\"via\":\"$f\"}"
      else
        echo "{\"ready\":false,\"via\":\"missing-pass-file\"}"
      fi
      ;;
    *) echo '{"ready":false,"via":"unknown"}' ;;
  esac
}

cmd_check() {
  python3 - "$LINKS" "$REPORT" "$MAG" "$CFG" <<'PY'
import json, os, subprocess, time, urllib.request
from pathlib import Path

import sys
links_path, report_path, mag, cfg = sys.argv[1:5]
data = json.loads(Path(links_path).read_text())
cfg = Path(cfg)
mag = Path(mag)

def http_code(url):
    try:
        req = urllib.request.Request(url, headers={"User-Agent": "BGRTX-WebManage/1.0", "Cache-Control": "no-cache"})
        with urllib.request.urlopen(req, timeout=12) as r:
            return r.status
    except Exception as e:
        # follow some errors
        if hasattr(e, "code"):
            return e.code
        return 0

def file_ok(spec):
    if not spec or not str(spec).startswith("file:"):
        return None
    p = spec[5:]
    if p.startswith("Projects/"):
        p = str(Path.home() / p)
    elif p.startswith("config/"):
        p = str(cfg / p[7:])
    elif p.startswith("/home/") or p.startswith("/"):
        pass
    return Path(p).exists()

def login_ready(kind):
    if kind == "none":
        return True, "none"
    if kind == "waterfox":
        wf = Path.home() / ".waterfox"
        ok = any(wf.rglob("logins.json"))
        return ok, "waterfox-logins" if ok else "waterfox-missing"
    if kind == "oauth-file":
        ok = (mag / ".wp-oauth.json").is_file() or (cfg / "wp-oauth.json").is_file()
        return ok, "oauth" if ok else "no-oauth"
    if kind == "titan-pass":
        f = cfg / "titan.pass"
        ok = f.is_file() and f.stat().st_size > 0
        return ok, "titan.pass" if ok else "missing-titan.pass"
    if kind == "app-pass":
        f = cfg / "bsky.app.pass"
        ok = f.is_file() and f.stat().st_size > 0
        return ok, "bsky.app.pass" if ok else "missing-bsky.app.pass"
    return False, "unknown"

results = []
for g in data.get("groups", []):
    for it in g.get("items", []):
        url = it.get("url") or ""
        check = it.get("check") or "none"
        code = None
        exists = None
        if check == "http" and url.startswith("http"):
            code = http_code(url)
        if isinstance(check, str) and check.startswith("file:"):
            exists = file_ok(check)
        if url.startswith("file://"):
            exists = Path(url[7:]).exists()
        ready, via = login_ready(it.get("login") or "none")
        ok = True
        if code is not None and code not in (200, 301, 302, 303, 307, 308):
            # wp-admin often 302 to login = still "up"
            if not (code in (401, 403) and "wp-admin" in url):
                if code == 0:
                    ok = False
        if exists is False:
            ok = False
        results.append({
            "group": g.get("id"),
            "id": it.get("id"),
            "title": it.get("title"),
            "url": url,
            "http": code,
            "file_ok": exists,
            "login": it.get("login"),
            "login_ready": ready,
            "login_via": via,
            "ok": ok and ready if it.get("login") not in (None, "none", "waterfox") else ok,
            "notes": it.get("notes"),
        })

# ports
import socket
def port_up(port):
    try:
        with socket.create_connection(("127.0.0.1", port), timeout=0.4):
            return True
    except Exception:
        return False

ports = {p: port_up(p) for p in (80, 18770, 18771, 18772, 5335)}
rep = {
    "ts": time.strftime("%Y-%m-%dT%H:%M:%S"),
    "law": data.get("law"),
    "ports": ports,
    "links": results,
    "summary": {
        "total": len(results),
        "http_ok": sum(1 for r in results if r["http"] in (200, 301, 302, 303, 307, 308)),
        "login_ready": sum(1 for r in results if r["login_ready"]),
        "login_not_ready": sum(1 for r in results if not r["login_ready"] and r["login"] not in (None, "none")),
    },
    "actions_on_reboot": [
        "desk-boot-restore.sh starts employee stack",
        "apache grok-employee :80",
        "desk-rest :18772",
        "scar-cook check",
        "bgrtx local-up optional",
        "Waterfox sessions restore when you open Waterfox",
    ],
}
Path(report_path).write_text(json.dumps(rep, indent=2))
print(json.dumps(rep["summary"], indent=2))
print("ports", ports)
print("wrote", report_path)
# human lines
for r in results:
    flag = "OK " if r["ok"] else "!! "
    login = "login:" + ("ready" if r["login_ready"] else "NEED")
    http = f"http={r['http']}" if r["http"] is not None else ""
    print(f"{flag}[{r['group']}] {r['title'][:32]:32} {login:12} {http}")
PY
}

cmd_links() {
  python3 - "$LINKS" <<'PY'
import json,sys
from pathlib import Path
d=json.loads(Path(sys.argv[1]).read_text())
for g in d.get("groups",[]):
  print(f"\n## {g.get('title')} ({g.get('id')})")
  for it in g.get("items",[]):
    print(f"  - {it.get('title')}: {it.get('url')}")
    if it.get("notes"):
      print(f"      {it['notes']}")
PY
}

cmd_open() {
  local group="${1:-employee}"
  local urls
  urls=$(python3 - "$LINKS" "$group" <<'PY'
import json,sys
from pathlib import Path
d=json.loads(Path(sys.argv[1]).read_text())
gid=sys.argv[2]
for g in d.get("groups",[]):
  if g.get("id")==gid or gid=="all":
    for it in g.get("items",[]):
      u=it.get("url") or ""
      if u.startswith("http"):
        print(u)
PY
)
  if [[ -z "$urls" ]]; then
    echo "unknown group: $group · try: public wordpress mail social employee desk all" >&2
    exit 2
  fi
  local browser=""
  if command -v waterfox >/dev/null; then browser=waterfox
  elif command -v firefox >/dev/null; then browser=firefox
  else browser=""; fi
  echo "Opening group=$group via ${browser:-echo}"
  while IFS= read -r u; do
    [[ -z "$u" ]] && continue
    echo "  $u"
    if [[ -n "$browser" ]]; then
      "$browser" "$u" >/dev/null 2>&1 &
      sleep 0.35
    fi
  done <<<"$urls"
}

cmd_boot() {
  echo "=== web-manage boot · employee stack ==="
  # apache employee (must stay up — do not stop)
  if systemctl is-enabled apache2 >/dev/null 2>&1 || systemctl is-active apache2 >/dev/null 2>&1; then
    sudo -n systemctl start apache2 2>/dev/null \
      || SUDO_ASKPASS="${SUDO_ASKPASS:-$ROOT/Build/sudo-askpass.sh}" SUDO_PASS="${SUDO_PASS:-mememe}" sudo -A systemctl start apache2 2>/dev/null \
      || true
    echo "apache: $(systemctl is-active apache2 2>/dev/null || echo ?)"
  fi
  bash "$ROOT/Build/desk-rest.sh" start || true
  if [[ -x "$MAG/bin/bgrtx" ]]; then
    bash "$MAG/bin/bgrtx" local-up >/dev/null 2>&1 || true
  fi
  bash "$ROOT/Build/scar-cook.sh" check >/dev/null 2>&1 || true
  cmd_check
  echo
  echo "Open employee: http://127.0.0.1/"
  echo "Login sessions: start Waterfox (saved passwords/sessions)"
  echo "Mail: Grok-Claws desktop or titan-mail status"
  echo "Bluesky: bsky status · App Password if posting"
}

cmd_status() {
  echo "=== Grok web management ==="
  echo "links: $LINKS"
  echo "report: $REPORT"
  echo "public: https://biggrinrtx.com (Atomic)"
  echo "employee: http://127.0.0.1/"
  echo
  echo "login files:"
  for f in wp-oauth.json titan.pass bsky.app.pass bsky.handle xai.key; do
    if [[ -f "$CFG/$f" ]] || [[ -f "$MAG/.$f" ]] || [[ -f "$MAG/$f" ]]; then
      echo "  IN  $f"
    else
      # special oauth path
      if [[ "$f" == wp-oauth.json && -f "$MAG/.wp-oauth.json" ]]; then echo "  IN  .wp-oauth.json (mag)"; continue; fi
      echo "  OFF $f"
    fi
  done
  echo
  cmd_check
}

cmd_html() {
  # regenerate links panel for employee www (with login readiness from latest report)
  local dest="${1:-$HOME/Projects/grok-employee/www/links.html}"
  python3 - "$LINKS" "$dest" "$REPORT" <<'PY'
import json,sys
from pathlib import Path
d=json.loads(Path(sys.argv[1]).read_text())
out=Path(sys.argv[2])
rep={}
try:
  rep=json.loads(Path(sys.argv[3]).read_text())
except Exception:
  pass
by={f"{r.get('group')}:{r.get('id')}":r for r in (rep.get("links") or [])}
parts=['<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1"/>',
'<title>Web links · Grok manage</title>',
'<style>',
':root{--bg:#050607;--panel:#0a0c0b;--line:#1e2428;--text:#e8e4df;--mute:#8a9298;--mint:#05f2a0;--cyan:#2de2e6;--gold:#e8c547;--pink:#ff2a6d}',
'body{font:14px/1.45 system-ui;background:linear-gradient(165deg,#050607,#0a0c0b 45%,#121416);color:var(--text);margin:0;padding:1rem;min-height:100vh}',
'a{color:var(--mint);text-decoration:none}a:hover{text-decoration:underline}',
'h1{font-size:1.2rem;margin:.4rem 0}h2{color:var(--cyan);font-size:12px;letter-spacing:.12em;text-transform:uppercase;margin:0 0 .6rem}',
'.g{margin:0 0 1rem;padding:12px;border:1px solid var(--line);border-radius:12px;background:var(--panel)}',
'li{margin:.4rem 0}.m{color:var(--mute);font-size:12px}.ok{color:var(--mint)}.need{color:var(--gold)}.bad{color:var(--pink)}',
'.pill{display:inline-block;padding:6px 12px;border-radius:999px;border:1px solid var(--line);background:var(--panel);color:var(--mute);font:700 11px system-ui;letter-spacing:.06em;text-transform:uppercase;margin:0 6px 6px 0;text-decoration:none}',
'.pill:hover{color:var(--mint)}.law{margin:0 0 1rem;padding:10px 12px;border-left:3px solid var(--gold);background:#121416;border-radius:0 10px 10px 0;color:#c4b8a8;font-size:13px}',
'</style></head><body>',
'<p><a class="pill" href="/">← Grok desk</a>',
'<a class="pill" href="https://biggrinrtx.com/" target="_blank" rel="noopener">Public Atomic</a>',
'<a class="pill" href="https://biggrinrtx.com/wp-admin/" target="_blank" rel="noopener">WP admin</a>',
'<a class="pill" href="https://app.titan.email/" target="_blank" rel="noopener">Titan</a>',
'<a class="pill" href="https://bsky.app/settings" target="_blank" rel="noopener">Bluesky</a>',
'<a class="pill" href="/api/links" target="_blank" rel="noopener">REST /links</a></p>',
'<h1>Web management · organized links</h1>',
f'<div class="law">{d.get("law","Public=Atomic · 127=employee · Waterfox logins")}<br/>',
'Reboot: <code>bgrtx boot</code> · Check: <code>bgrtx web check</code> · Open group: <code>bgrtx web open wordpress</code></div>']
summ=rep.get("summary") or {}
if summ:
  parts.append(f'<p class="m">last check: total={summ.get("total")} · http_ok={summ.get("http_ok")} · login_ready={summ.get("login_ready")} · login_need={summ.get("login_not_ready")}</p>')
for g in d.get("groups",[]):
  parts.append(f'<div class="g"><h2>{g.get("title")}</h2><ul>')
  for it in g.get("items",[]):
    u=it.get("url") or "#"
    key=f'{g.get("id")}:{it.get("id")}'
    r=by.get(key) or {}
    login=it.get("login") or "none"
    ready=r.get("login_ready")
    if ready is None and login=="none":
      ready=True
    badge=""
    if login and login!="none":
      if ready:
        badge=' <span class="ok">· login ready</span>'
      else:
        badge=' <span class="need">· login NEED</span>'
    http=r.get("http")
    httpb=f' <span class="m">http {http}</span>' if http is not None else ""
    if u.startswith("desk:") or u.startswith("file://"):
      parts.append(f'<li><strong>{it.get("title")}</strong> <span class="m">{u}</span>{badge}{httpb}')
    else:
      parts.append(f'<li><a href="{u}" target="_blank" rel="noopener">{it.get("title")}</a>{badge}{httpb}')
    if it.get("notes"):
      parts.append(f' <span class="m">— {it["notes"]}</span>')
    parts.append('</li>')
  parts.append('</ul></div>')
parts.append('<div class="g"><h2>On reboot</h2><ul class="m">')
for a in (rep.get("actions_on_reboot") or [
  "bgrtx boot · desk-boot-restore starts employee stack",
  "apache grok-employee :80",
  "desk-rest :18772",
  "web-manage check → out/web_manage_latest.json",
  "Waterfox sessions restore when you open Waterfox",
]):
  parts.append(f'<li>{a}</li>')
parts.append('</ul></div>')
parts.append('<p class="m">CONFIDENCE CREATES WHEN GRIN · free thrift · remain IN · God Bless</p>')
parts.append('</body></html>')
out.parent.mkdir(parents=True, exist_ok=True)
out.write_text("".join(parts))
print("wrote", out)
PY
}

case "$cmd" in
  status|st) cmd_status ;;
  check|report) cmd_check ;;
  links|list) cmd_links ;;
  open) cmd_open "${1:-employee}" ;;
  boot|reboot-check) cmd_boot ;;
  html|panel) cmd_html "$@" ;;
  help|-h|--help)
    cat <<EOF
web-manage · Grok web organization
  status   pass-file readiness + link check
  check    HTTP + login readiness report → out/web_manage_latest.json
  links    print organized URLs
  open G   open group in Waterfox (public|wordpress|mail|social|employee|desk|all)
  boot     start employee stack + scar check + report (post-reboot)
  html     write links.html for 127 desk
EOF
    ;;
  *) echo "unknown $cmd · web-manage help" >&2; exit 2 ;;
esac
