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

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
STACK_ENV="${STACK_ENV:-$ROOT_DIR/stack.env}"

STACK_ID="${GIA_STACK_ID:-${STACK_ID:-}}"
STACK_ID="$(echo "$STACK_ID" | tr -cs 'a-zA-Z0-9._-' '-' | sed 's/^-*//; s/-*$//')"

name_with_stack() {
  local base="$1"
  if [[ -n "$STACK_ID" ]]; then
    echo "${base}_${STACK_ID}"
  else
    echo "$base"
  fi
}

POD_NAME="$(name_with_stack "gia")"

REDIS_CONTAINER="$(name_with_stack "redis_gia")"
SIGNAL_CONTAINER="$(name_with_stack "signal")"
MIGRATION_CONTAINER="$(name_with_stack "migration_gia")"
COLLECTSTATIC_CONTAINER="$(name_with_stack "collectstatic_gia")"
APP_CONTAINER="$(name_with_stack "gia")"
ASGI_CONTAINER="$(name_with_stack "asgi_gia")"
UR_CONTAINER="$(name_with_stack "ur_gia")"
SCHED_CONTAINER="$(name_with_stack "scheduling_gia")"
PROSODY_CONTAINER="$(name_with_stack "prosody_gia")"

REDIS_DATA_DIR="${QUADLET_REDIS_DATA_DIR:-$ROOT_DIR/.podman/gia_redis_data}"
WHATSAPP_DATA_DIR="${QUADLET_WHATSAPP_DATA_DIR:-$ROOT_DIR/.podman/gia_whatsapp_data}"
SQLITE_DATA_DIR="${QUADLET_SQLITE_DATA_DIR:-$ROOT_DIR/.podman/gia_sqlite_data}"
PROSODY_CONFIG_FILE=""  # resolved after load_env in start_stack
PROSODY_CERTS_DIR="${QUADLET_PROSODY_CERTS_DIR:-$ROOT_DIR/.podman/gia_prosody_certs}"
PROSODY_DATA_DIR="${QUADLET_PROSODY_DATA_DIR:-$ROOT_DIR/.podman/gia_prosody_data}"
PROSODY_LOGS_DIR="${QUADLET_PROSODY_LOGS_DIR:-$ROOT_DIR/.podman/gia_prosody_logs}"
PROSODY_IMAGE="${PROSODY_IMAGE:-docker.io/prosody/prosody:latest}"
if id code >/dev/null 2>&1; then
  PROSODY_RUN_USER_DEFAULT="$(id -u code):$(id -g code)"
else
  PROSODY_RUN_USER_DEFAULT="$(id -u):$(id -g)"
fi
PROSODY_RUN_USER="${PROSODY_RUN_USER:-$PROSODY_RUN_USER_DEFAULT}"
PROSODY_ENABLED="${PROSODY_ENABLED:-false}"
ENSURE_XMPP_SECRET_SCRIPT="$ROOT_DIR/utilities/prosody/ensure_xmpp_secret.sh"
if [[ -n "${STACK_ID}" ]]; then
  VRUN_DIR="/code/vrun/${STACK_ID}"
else
  VRUN_DIR="/code/vrun"
fi

load_env() {
  set -a
  . "$STACK_ENV"
  set +a
  PROSODY_ENABLED="${PROSODY_ENABLED:-false}"
  if [[ -x "$ENSURE_XMPP_SECRET_SCRIPT" ]]; then
    XMPP_SECRET="$("$ENSURE_XMPP_SECRET_SCRIPT" "$STACK_ENV")"
    export XMPP_SECRET
  fi
}

is_remote() {
  [[ -n "${CONTAINER_HOST:-}" ]] || [[ -n "${PODMAN_HOST:-}" ]] || [[ -n "${DOCKER_HOST:-}" ]]
}

resolve_path() {
  local path="$1"
  if [[ "$path" = /* ]]; then
    echo "$path"
  else
    echo "$ROOT_DIR/$path"
  fi
}

require_podman() {
  if ! command -v podman >/dev/null 2>&1; then
    echo "podman not found" >&2
    exit 1
  fi
}

ensure_dirs() {
  mkdir -p "$REDIS_DATA_DIR" "$WHATSAPP_DATA_DIR" "$SQLITE_DATA_DIR" "$VRUN_DIR" "$ROOT_DIR/signal-cli-config" "$PROSODY_CERTS_DIR" "$PROSODY_DATA_DIR" "$PROSODY_LOGS_DIR"
  chmod 0777 "$SQLITE_DATA_DIR" 2>/dev/null || true
  # Container runs as uid 1000 (xf); rootless Podman remaps uids so plain
  # chown won't work — podman unshare translates to the correct host uid.
  if [[ -n "${QUADLET_SKIP_UNSHARE:-}" ]] || [[ -n "${CONTAINER_HOST:-}" ]] || [[ -n "${PODMAN_HOST:-}" ]] || [[ -n "${DOCKER_HOST:-}" ]]; then
    echo "Skipping podman unshare chown -- running in sandbox"
  else
    podman unshare chown 1000:1000 "$WHATSAPP_DATA_DIR" 2>/dev/null || true
  fi
}

rm_if_exists() {
  podman rm -f "$1" >/dev/null 2>&1 || true
}

wait_for_redis_socket() {
  local sock="$VRUN_DIR/gia-redis.sock"
  local i
  for i in $(seq 1 60); do
    [[ -S "$sock" ]] && return 0
    sleep 1
  done
  echo "redis socket did not appear at $sock" >&2
  return 1
}

run_worker_container() {
  local name="$1"
  local cmd="$2"
  local with_uwsgi="${3:-0}"
  local with_whatsapp="${4:-0}"
  local cpu_limit="${5:-0.5}"

  rm_if_exists "$name"
  local args=(
    --replace
    --name "$name"
    --pod "$POD_NAME"
    --cpus "$cpu_limit"
    --user "$(id -u):$(id -g)"
    --env-file "$STACK_ENV"
    --env "SIGNAL_HTTP_URL=http://127.0.0.1:8080"
    --env "APP_DATABASE_PATH=$APP_DATABASE_PATH"
    -v "$REPO_DIR:/code"
    -v "$SQLITE_DATA_DIR:/conf"
    -v "$VRUN_DIR:/var/run"
  )
  if [[ "$with_uwsgi" == "1" ]]; then
    args+=( -v "$REPO_DIR/docker/uwsgi.ini:/conf/uwsgi.ini:ro" )
  fi
  if [[ "$with_whatsapp" == "1" ]]; then
    args+=( -v "$WHATSAPP_DATA_DIR:${WHATSAPP_DB_DIR:-/var/tmp/whatsapp}" )
  fi
  podman run -d "${args[@]}" localhost/xf/gia:prod sh -c "$cmd" >/dev/null
}

run_oneshot_container() {
  local name="$1"
  local cmd="$2"
  local with_whatsapp="${3:-0}"

  rm_if_exists "$name"
  local args=(
    --replace
    --name "$name"
    --pod "$POD_NAME"
    --cpus "0.5"
    --user "$(id -u):$(id -g)"
    --env-file "$STACK_ENV"
    --env "SIGNAL_HTTP_URL=http://127.0.0.1:8080"
    --env "APP_DATABASE_PATH=$APP_DATABASE_PATH"
    -v "$REPO_DIR:/code"
    -v "$SQLITE_DATA_DIR:/conf"
    -v "$VRUN_DIR:/var/run"
  )
  if [[ "$with_whatsapp" == "1" ]]; then
    args+=( -v "$WHATSAPP_DATA_DIR:${WHATSAPP_DB_DIR:-/var/tmp/whatsapp}" )
  fi
  podman run "${args[@]}" localhost/xf/gia:prod sh -c "$cmd" >/dev/null
}

down_stack() {
  podman pod rm -f "$POD_NAME" >/dev/null 2>&1 || true
  rm_if_exists "$REDIS_CONTAINER"
  rm_if_exists "$SIGNAL_CONTAINER"
  rm_if_exists "$MIGRATION_CONTAINER"
  rm_if_exists "$COLLECTSTATIC_CONTAINER"
  rm_if_exists "$APP_CONTAINER"
  rm_if_exists "$ASGI_CONTAINER"
  rm_if_exists "$UR_CONTAINER"
  rm_if_exists "$SCHED_CONTAINER"
  rm_if_exists "$PROSODY_CONTAINER"
}

start_stack() {
  require_podman
  load_env
  PROSODY_CONFIG_FILE="${QUADLET_PROSODY_CONFIG_FILE:-$ROOT_DIR/utilities/prosody/prosody.cfg.lua}"
  REPO_DIR="$(resolve_path "$REPO_DIR")"
  APP_DATABASE_FILE="$(resolve_path "$APP_DATABASE_FILE")"
  APP_DATABASE_BASENAME="$(basename "$APP_DATABASE_FILE")"
  APP_DATABASE_PATH="/conf/$APP_DATABASE_BASENAME"
  HOST_DATABASE_FILE="$SQLITE_DATA_DIR/$APP_DATABASE_BASENAME"
  APP_LOCAL_SETTINGS="$(resolve_path "$APP_LOCAL_SETTINGS")"
  ensure_dirs
  if [[ "$APP_DATABASE_FILE" != "$HOST_DATABASE_FILE" ]] && [[ -f "$APP_DATABASE_FILE" ]] && [[ ! -f "$HOST_DATABASE_FILE" ]]; then
    cp "$APP_DATABASE_FILE" "$HOST_DATABASE_FILE"
  fi
  touch "$HOST_DATABASE_FILE"
  chmod 0666 "$HOST_DATABASE_FILE" 2>/dev/null || true
  down_stack
  # Brief pause so the OS releases port bindings from the previous Prosody
  # process before new containers try to bind them.
  sleep 2

  local port_offset="${GIA_STACK_PORT_OFFSET:-}"
  if [[ -z "$port_offset" && -n "$STACK_ID" ]]; then
    port_offset="$(( $(printf '%s' "$STACK_ID" | cksum | awk '{print $1}') % 500 + 1 ))"
  fi
  port_offset="${port_offset:-0}"
  local app_port="${APP_PORT:-$((5006 + port_offset))}"
  local signal_port="${SIGNAL_PUBLIC_PORT:-$((8080 + port_offset))}"
  local prosody_c2s_port="${PROSODY_PUBLIC_C2S_PORT:-5222}"
  local prosody_s2s_port="${PROSODY_PUBLIC_S2S_PORT:-5269}"
  local prosody_component_port="${PROSODY_PUBLIC_COMPONENT_PORT:-8888}"
  local prosody_http_port="${PROSODY_PUBLIC_HTTP_PORT:-5280}"

  local pod_args=(--name "$POD_NAME" -p "${app_port}:8000" -p "${signal_port}:8080")
  if [[ "$PROSODY_ENABLED" == "true" ]]; then
    pod_args+=(
      -p "${prosody_c2s_port}:5222"
      -p "${prosody_s2s_port}:5269"
      -p "${prosody_component_port}:8888"
      -p "${prosody_http_port}:5280"
    )
  fi
  podman pod create "${pod_args[@]}" >/dev/null

  podman run -d \
    --replace \
    --name "$REDIS_CONTAINER" \
    --pod "$POD_NAME" \
    --cpus "0.1" \
    -v "$REPO_DIR/docker/redis.conf:/etc/redis.conf:ro" \
    -v "$REDIS_DATA_DIR:/data" \
    -v "$VRUN_DIR:/var/run" \
    docker.io/library/redis:latest \
    redis-server /etc/redis.conf >/dev/null

  podman run -d \
    --replace \
    --name "$SIGNAL_CONTAINER" \
    --pod "$POD_NAME" \
    --cpus "0.2" \
    -e MODE=native \
    -v "$ROOT_DIR/signal-cli-config:/home/.local/share/signal-cli" \
    localhost/gia-signal:latest >/dev/null

  if [[ "$PROSODY_ENABLED" == "true" ]]; then
    podman run -d \
      --replace \
      --name "$PROSODY_CONTAINER" \
      --pod "$POD_NAME" \
      --cpus "0.2" \
      --env-file "$STACK_ENV" \
      --user "$PROSODY_RUN_USER" \
      --entrypoint prosody \
      -v "$PROSODY_CONFIG_FILE:/etc/prosody/prosody.cfg.lua:ro" \
      -v "$PROSODY_CERTS_DIR:/etc/prosody/certs" \
      -v "$PROSODY_DATA_DIR:/var/lib/prosody" \
      -v "$PROSODY_LOGS_DIR:/var/log/prosody" \
      -v "$REPO_DIR:/code" \
      "$PROSODY_IMAGE" \
      -F >/dev/null
  fi

  wait_for_redis_socket

  run_oneshot_container "$MIGRATION_CONTAINER" ". /venv/bin/activate && python manage.py migrate --noinput"
  run_oneshot_container "$COLLECTSTATIC_CONTAINER" ". /venv/bin/activate && python manage.py collectstatic --noinput"

  run_worker_container "$APP_CONTAINER" "if [ \"\$OPERATION\" = \"uwsgi\" ] ; then . /venv/bin/activate && uwsgi --ini /conf/uwsgi.ini ; else . /venv/bin/activate && exec python manage.py runserver 0.0.0.0:8000; fi" 1 1 0.5
  run_worker_container "$ASGI_CONTAINER" "rm -f /var/run/asgi-gia.sock && . /venv/bin/activate && python -m pip install --disable-pip-version-check -q uvicorn && python -m uvicorn app.asgi:application --uds /var/run/asgi-gia.sock --workers 1" 0 1 0.3
  run_worker_container "$UR_CONTAINER" ". /venv/bin/activate && python manage.py ur" 1 1 0.5
  run_worker_container "$SCHED_CONTAINER" ". /venv/bin/activate && python manage.py scheduling" 1 0 0.3
}

render_units() {
  python3 "$ROOT_DIR/scripts/quadlet/render_units.py" --stack-env "$STACK_ENV"
}

case "${1:-}" in
  install)
    render_units
    ;;
  up)
    start_stack
    trap 'down_stack; exit 0' INT TERM
    if is_remote; then
      podman logs -f "$APP_CONTAINER" || true
    else
      podman logs -f "$APP_CONTAINER" "$ASGI_CONTAINER" "$UR_CONTAINER" "$SCHED_CONTAINER" "$REDIS_CONTAINER" "$SIGNAL_CONTAINER" || true
    fi
    ;;
  down)
    require_podman
    down_stack
    ;;
  restart)
    start_stack
    ;;
  status)
    require_podman
    load_env
    podman pod ps --format "table {{.Name}}\t{{.Status}}" | grep -E "^$POD_NAME\b" || true
    podman ps --format "table {{.Names}}\t{{.Status}}" | grep -E "^($APP_CONTAINER|$ASGI_CONTAINER|$UR_CONTAINER|$SCHED_CONTAINER|$REDIS_CONTAINER|$SIGNAL_CONTAINER|$PROSODY_CONTAINER)\b" || true
    ;;
  logs)
    require_podman
    load_env
    if is_remote; then
      podman logs -f "$APP_CONTAINER"
    else
      log_targets=("$APP_CONTAINER" "$ASGI_CONTAINER" "$UR_CONTAINER" "$SCHED_CONTAINER" "$REDIS_CONTAINER" "$SIGNAL_CONTAINER")
      if [[ "$PROSODY_ENABLED" == "true" ]]; then
        log_targets+=("$PROSODY_CONTAINER")
      fi
      podman logs -f "${log_targets[@]}"
    fi
    ;;
  watch)
    require_podman
    load_env
    exec "$ROOT_DIR/scripts/quadlet/watchdog.sh"
    ;;
  *)
    echo "Usage: $0 {install|up|down|restart|status|logs|watch}" >&2
    exit 2
    ;;
esac
