#!/usr/bin/env bash
# =====================================================================
# LogShip template — step 5 (on every clone, after 03-neutralize-env.sh): make the
# instance's .env EFFECTIVE without a rebuild.
#
# Every `runtimeConfig` value read from `process.env.X` in nuxt.config.ts is baked into
# .output/server/chunks/nitro/nitro.mjs at BUILD time (on prod, with prod's .env). A clone
# therefore keeps prod's service token, Laravel URL, carrier/marketplace credentials,
# Sentry DSN and public URLs no matter what 03-neutralize-env.sh writes — only Nitro's
# runtime override layer (`NUXT_<PATH>` env vars, read at every start) changes them
# (see .claude/rules/ops/runtimeconfig-env-reload.md).
#
# This script parses the runtimeConfig block of the deployed nuxt.config.ts, resolves each
# `process.env.VAR` leaf against the instance .env (honouring `|| 'default'`, `Number(... || n)`,
# `=== 'true'`, `!== 'false'`) and (re)writes a managed block of NUXT_* lines at the end of
# .env. ecosystem.config.cjs loads .env into the process env, so a plain
#   pm2 delete erpfrontend && pm2 start ecosystem.config.cjs --only erpfrontend --update-env
# (NOT `pm2 restart`, which keeps the old env) applies them. Idempotent; run after every
# .env edit on an instance that was not rebuilt with that .env.
#
#   05-apply-env-overrides.sh [--dry-run] [ENV_FILE] [NUXT_CONFIG]
# =====================================================================
set -euo pipefail
DRY=0; [ "${1:-}" = "--dry-run" ] && { DRY=1; shift; }
ENVF="${1:-/opt/erp-nuxt-frontend/.env}"
CFG="${2:-/opt/erp-nuxt-frontend/nuxt.config.ts}"
[ -f "$ENVF" ] && [ -f "$CFG" ] || { echo "FATAL: $ENVF or $CFG missing" >&2; exit 1; }

python3 - "$ENVF" "$CFG" "$DRY" <<'PY'
import re, sys
envf, cfg, dry = sys.argv[1], sys.argv[2], sys.argv[3] == "1"
BEGIN = "# >>> nitro runtime overrides (generated by 05-apply-env-overrides.sh — do not edit, re-run the script) >>>"
END = "# <<< nitro runtime overrides <<<"

# --- .env values ------------------------------------------------------------------------------
env = {}
raw = open(envf, encoding="utf-8").read().split("\n")
for l in raw:
    m = re.match(r"^([A-Z_0-9]+)=(.*)$", l)
    if not m: continue
    v = m.group(2).strip()
    if len(v) >= 2 and v[0] == v[-1] and v[0] in "'\"": v = v[1:-1]
    env[m.group(1)] = v

# --- runtimeConfig block of nuxt.config.ts ------------------------------------------------------
src = open(cfg, encoding="utf-8").read().split("\n")
start = next(i for i, l in enumerate(src) if re.match(r"^\s*runtimeConfig:\s*\{\s*$", l))
indent = len(src[start]) - len(src[start].lstrip())
path, leaves = [], []
def snake(s):  # scule snakeCase: camelCase -> camel_case, keeps digits attached
    s = re.sub(r"([a-z0-9])([A-Z])", r"\1_\2", s)
    s = re.sub(r"([A-Z]+)([A-Z][a-z])", r"\1_\2", s)
    return s.upper()
for l in src[start + 1:]:
    stripped = l.strip()
    if re.match(r"^\},?$", stripped):
        if not path: break
        path.pop(); continue
    if not stripped or stripped.startswith("//"): continue
    m = re.match(r"^(\w+):\s*\{\s*$", stripped)
    if m: path.append(m.group(1)); continue
    m = re.match(r"^(\w+):\s*(.+?),?\s*(//.*)?$", stripped)
    if not m: continue
    key, expr = m.group(1), m.group(2)
    vars_ = re.findall(r"process\.env\.([A-Z_0-9]+)", expr)
    if not vars_: continue
    var = vars_[-1]                      # non-capacitor branch of `isCapacitor ? … : process.env.X`
    v = env.get(var)
    if "=== 'true'" in expr:   out = "true" if v == "true" else "false"
    elif "!== 'false'" in expr: out = "false" if v == "false" else "true"
    else:
        dflt = re.search(r"\|\|\s*('([^']*)'|\"([^\"]*)\"|([0-9.]+))", expr) or re.search(r"\?\?\s*('([^']*)'|\"([^\"]*)\"|([0-9.]+))", expr)
        if v: out = v
        elif dflt: out = dflt.group(2) if dflt.group(2) is not None else (dflt.group(3) if dflt.group(3) is not None else dflt.group(4))
        else: out = ""
    name = "NUXT_" + "_".join(snake(p) for p in path + [key])
    leaves.append((name, var, out))

# --- rewrite the managed block --------------------------------------------------------------------
lines = raw
if BEGIN in lines:
    a, b = lines.index(BEGIN), lines.index(END)
    lines = lines[:a] + lines[b + 1:]
managed = {n for n, _, _ in leaves}
lines = [l for l in lines if not (re.match(r"^(NUXT_[A-Z_0-9]+)=", l) and re.match(r"^(NUXT_[A-Z_0-9]+)=", l).group(1) in managed)]
while lines and lines[-1] == "": lines.pop()
block = [BEGIN] + [f"{n}='{o.replace(chr(39), chr(39)+chr(92)+chr(39)+chr(39))}'" for n, _, o in leaves] + [END, ""]
if dry:
    for n, var, o in leaves: print(f"{n:48s} <- {var:34s} = {o[:60]!r}")
    print(f"\n{len(leaves)} overrides (dry run, {envf} unchanged)")
else:
    open(envf, "w", encoding="utf-8").write("\n".join(lines + [""] + block))
    print(f"{len(leaves)} NUXT_* overrides written to {envf}. Apply with:\n  cd /opt/erp-nuxt-frontend && pm2 delete erpfrontend; pm2 start ecosystem.config.cjs --only erpfrontend --update-env")
PY
