#!/usr/bin/env bash
set -euo pipefail

APP_PUBLIC_PORT="${APP_PUBLIC_PORT:-8080}"
APP_FRONTEND_PORT="${APP_FRONTEND_PORT:-3000}"
APP_BACKEND_PORT="${APP_BACKEND_PORT:-18080}"
UVICORN_LOOP="${UVICORN_LOOP:-asyncio}"
UVICORN_HTTP="${UVICORN_HTTP:-h11}"
UVICORN_WS="${UVICORN_WS:-websockets}"
BEAVER_HOME="${BEAVER_HOME:-/root/.beaver}"
BEAVER_CONFIG_PATH="${BEAVER_CONFIG_PATH:-$BEAVER_HOME/config.json}"
BEAVER_WORKSPACE="${BEAVER_WORKSPACE:-$BEAVER_HOME/workspace}"
BEAVER_AUTH_FILE="${BEAVER_AUTH_FILE:-$BEAVER_HOME/web_auth_users.json}"
BEAVER_RUNTIME_ENV_FILE="${BEAVER_RUNTIME_ENV_FILE:-$BEAVER_HOME/runtime.env}"
BEAVER_INITIAL_SKILLS_DIR="${BEAVER_INITIAL_SKILLS_DIR:-/opt/app/initial-skills}"
BEAVER_INITIAL_SKILLS_EXCLUDE="${BEAVER_INITIAL_SKILLS_EXCLUDE:-officebench-mcp}"

log() {
  printf '[app-instance] %s\n' "$*"
}

require_file() {
  local path="$1"
  local message="$2"
  if [[ ! -f "$path" ]]; then
    printf '[app-instance] %s: %s\n' "$message" "$path" >&2
    exit 1
  fi
}

seed_initial_skills() {
  local initial_skills_dir="$1"
  local target_dir="$2"

  if [[ ! -d "$initial_skills_dir" ]]; then
    return
  fi
  if [[ ! -f "$initial_skills_dir/_index/published.json" ]]; then
    log "initial skills source has no published index, skipping: ${initial_skills_dir}"
    return
  fi

  mkdir -p "$target_dir"
  INITIAL_SKILLS_DIR="$initial_skills_dir" TARGET_DIR="$target_dir" INITIAL_SKILLS_EXCLUDE="$BEAVER_INITIAL_SKILLS_EXCLUDE" python - <<'PY'
import json
import os
import shutil
from pathlib import Path

initial = Path(os.environ["INITIAL_SKILLS_DIR"]).resolve()
target = Path(os.environ["TARGET_DIR"]).resolve()
excluded = {item.strip() for item in os.environ.get("INITIAL_SKILLS_EXCLUDE", "").split(",") if item.strip()}

for child in sorted(initial.iterdir()):
    if child.name.startswith(".") or child.name in excluded:
        continue
    destination = target / child.name
    if destination.exists():
        continue
    if child.is_dir():
        shutil.copytree(child, destination)
    elif child.is_file():
        shutil.copy2(child, destination)

for index_name in ("published", "disabled"):
    initial_index = initial / "_index" / f"{index_name}.json"
    target_index = target / "_index" / f"{index_name}.json"
    if not initial_index.exists():
        continue
    try:
        initial_items = json.loads(initial_index.read_text(encoding="utf-8")).get("items", [])
    except json.JSONDecodeError:
        initial_items = []
    if target_index.exists():
        try:
            target_items = json.loads(target_index.read_text(encoding="utf-8")).get("items", [])
        except json.JSONDecodeError:
            target_items = []
    else:
        target_items = []
    merged = []
    for item in [*target_items, *initial_items]:
        text = str(item).strip()
        if text in excluded:
            continue
        if text and text not in merged:
            merged.append(text)
    target_index.parent.mkdir(parents=True, exist_ok=True)
    target_index.write_text(json.dumps({"items": merged}, indent=2, ensure_ascii=False) + "\n", encoding="utf-8")
PY
}

cleanup() {
  local status=$?

  if [[ -n "${NGINX_PID:-}" ]]; then
    kill "${NGINX_PID}" 2>/dev/null || true
  fi
  if [[ -n "${FRONTEND_PID:-}" ]]; then
    kill "${FRONTEND_PID}" 2>/dev/null || true
  fi
  if [[ -n "${BACKEND_PID:-}" ]]; then
    kill "${BACKEND_PID}" 2>/dev/null || true
  fi

  wait 2>/dev/null || true
  exit "$status"
}

trap cleanup EXIT INT TERM

mkdir -p "$BEAVER_HOME" "$BEAVER_WORKSPACE"

if [[ -f "$BEAVER_RUNTIME_ENV_FILE" ]]; then
  set -a
  . "$BEAVER_RUNTIME_ENV_FILE"
  set +a
fi

require_file "$BEAVER_CONFIG_PATH" "Missing Beaver config"
seed_initial_skills "$BEAVER_INITIAL_SKILLS_DIR" "$BEAVER_WORKSPACE/skills"

export BEAVER_AUTH_FILE
export BEAVER_RUNTIME_ENV_FILE
export BEAVER_HOME
export BEAVER_CONFIG_PATH
export BEAVER_WORKSPACE
export BEAVER_INITIAL_SKILLS_DIR
export BEAVER_INITIAL_SKILLS_EXCLUDE
export PORT="$APP_FRONTEND_PORT"
export HOSTNAME="127.0.0.1"
export PYTHONFAULTHANDLER="${PYTHONFAULTHANDLER:-1}"

log "starting Beaver backend on 127.0.0.1:${APP_BACKEND_PORT} (loop=${UVICORN_LOOP}, http=${UVICORN_HTTP}, ws=${UVICORN_WS})"
(
  cd /opt/app/backend
  python -m uvicorn "beaver.interfaces.web.app:create_app" --factory --host 127.0.0.1 --port "$APP_BACKEND_PORT" --loop "$UVICORN_LOOP" --http "$UVICORN_HTTP" --ws "$UVICORN_WS"
) &
BACKEND_PID=$!

log "starting frontend on 127.0.0.1:${APP_FRONTEND_PORT}"
(
  cd /opt/app/frontend
  node server.js
) &
FRONTEND_PID=$!

log "starting nginx on 0.0.0.0:${APP_PUBLIC_PORT}"
nginx -c /opt/app/nginx.conf -g 'daemon off;' &
NGINX_PID=$!

wait -n "$BACKEND_PID" "$FRONTEND_PID" "$NGINX_PID"
