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

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
ENV_FILE="$ROOT_DIR/.env"
ENV_EXAMPLE_FILE="$ROOT_DIR/.env.example"
DOCKER_BIN="${DOCKER_BIN:-docker}"
read -r -a DOCKER_CMD <<<"$DOCKER_BIN"

require_cmd() {
  if ! command -v "$1" >/dev/null 2>&1; then
    echo "Missing dependency: $1" >&2
    exit 1
  fi
}

upsert_env() {
  local file="$1"
  local key="$2"
  local value="$3"
  local tmp found line
  tmp="$(mktemp)"
  found=0

  if [[ -f "$file" ]]; then
    while IFS= read -r line || [[ -n "$line" ]]; do
      if [[ "$line" == "$key="* ]]; then
        printf '%s=%s\n' "$key" "$value" >>"$tmp"
        found=1
      else
        printf '%s\n' "$line" >>"$tmp"
      fi
    done <"$file"
  fi

  if [[ "$found" -eq 0 ]]; then
    printf '%s=%s\n' "$key" "$value" >>"$tmp"
  fi

  mv "$tmp" "$file"
}

get_env_value() {
  local file="$1"
  local key="$2"
  if [[ ! -f "$file" ]]; then
    return 0
  fi
  grep -E "^${key}=" "$file" | tail -n 1 | cut -d= -f2- || true
}

looks_placeholder() {
  local value="$1"
  if [[ -z "$value" ]]; then
    return 0
  fi
  case "$value" in
    replace-with-* | *replace-with* | changeme | example | ... )
      return 0
      ;;
    *)
      return 1
      ;;
  esac
}

random_hex() {
  local bytes="${1:-32}"
  if command -v openssl >/dev/null 2>&1; then
    openssl rand -hex "$bytes"
    return 0
  fi

  node -e "process.stdout.write(require('node:crypto').randomBytes(${bytes}).toString('hex'))"
}

wait_for_health() {
  local url="$1"
  local retries="$2"
  local delay="$3"
  local i

  for i in $(seq 1 "$retries"); do
    if command -v curl >/dev/null 2>&1; then
      if curl --silent --show-error --fail "$url" >/dev/null 2>&1; then
        return 0
      fi
    else
      if node -e "fetch(process.argv[1]).then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))" "$url"; then
        return 0
      fi
    fi
    sleep "$delay"
  done

  return 1
}

docker_cmd() {
  "${DOCKER_CMD[@]}" "$@"
}

prepare_data_dir() {
  local data_dir="$ROOT_DIR/data"
  mkdir -p "$data_dir"
  mkdir -p "$data_dir/tmp" "$data_dir/ms-playwright" "$data_dir/npm-cache" "$data_dir/.cache"

  # The runtime container runs as user "node" (uid/gid 1000).
  # If setup is executed as root, fix bind-mount ownership to avoid EACCES at runtime.
  if [[ "$(id -u)" -eq 0 ]]; then
    chown -R 1000:1000 "$data_dir" 2>/dev/null || true
  fi

  if [[ ! -w "$data_dir" ]]; then
    echo "WARNING: $data_dir is not writable. App may fail with 500 on /dashboard." >&2
    echo "Run: sudo chown -R 1000:1000 $data_dir" >&2
  fi
}

ensure_data_dir_writable_for_runtime() {
  local data_dir="$ROOT_DIR/data"

  if docker_cmd run --rm --user 1000:1000 -v "$data_dir:/target" eggent:local \
    sh -lc "mkdir -p /target/tmp /target/ms-playwright /target/npm-cache /target/.cache && test -w /target && test -w /target/tmp && test -w /target/ms-playwright && test -w /target/npm-cache && test -w /target/.cache" >/dev/null 2>&1; then
    return 0
  fi

  docker_cmd run --rm --user 0:0 -v "$data_dir:/target" eggent:local \
    sh -lc "chown -R 1000:1000 /target" >/dev/null 2>&1 || true

  if docker_cmd run --rm --user 1000:1000 -v "$data_dir:/target" eggent:local \
    sh -lc "mkdir -p /target/tmp /target/ms-playwright /target/npm-cache /target/.cache && test -w /target && test -w /target/tmp && test -w /target/ms-playwright && test -w /target/npm-cache && test -w /target/.cache" >/dev/null 2>&1; then
    return 0
  fi

  echo "ERROR: data directory/cache paths are not writable for runtime user (uid 1000)." >&2
  echo "Fix and rerun:" >&2
  echo "  sudo chown -R 1000:1000 $data_dir" >&2
  exit 1
}

echo "==> Docker setup (isolated)"
require_cmd "${DOCKER_CMD[0]}"
if ! docker_cmd compose version >/dev/null 2>&1; then
  echo "Docker Compose v2 is required (docker compose ...)." >&2
  exit 1
fi

cd "$ROOT_DIR"

if [[ ! -f "$ENV_FILE" ]]; then
  if [[ ! -f "$ENV_EXAMPLE_FILE" ]]; then
    echo "Missing template file: $ENV_EXAMPLE_FILE" >&2
    exit 1
  fi
  cp "$ENV_EXAMPLE_FILE" "$ENV_FILE"
  echo "Created .env from .env.example"
fi

EXTERNAL_API_TOKEN_VALUE="$(get_env_value "$ENV_FILE" "EXTERNAL_API_TOKEN")"
if looks_placeholder "$EXTERNAL_API_TOKEN_VALUE"; then
  upsert_env "$ENV_FILE" "EXTERNAL_API_TOKEN" "$(random_hex 32)"
  echo "Generated EXTERNAL_API_TOKEN in .env"
fi

TELEGRAM_WEBHOOK_SECRET_VALUE="$(get_env_value "$ENV_FILE" "TELEGRAM_WEBHOOK_SECRET")"
if looks_placeholder "$TELEGRAM_WEBHOOK_SECRET_VALUE"; then
  upsert_env "$ENV_FILE" "TELEGRAM_WEBHOOK_SECRET" "$(random_hex 24)"
  echo "Generated TELEGRAM_WEBHOOK_SECRET in .env"
fi

EGGENT_AUTH_SECRET_VALUE="$(get_env_value "$ENV_FILE" "EGGENT_AUTH_SECRET")"
if looks_placeholder "$EGGENT_AUTH_SECRET_VALUE"; then
  upsert_env "$ENV_FILE" "EGGENT_AUTH_SECRET" "$(random_hex 32)"
  echo "Generated EGGENT_AUTH_SECRET in .env"
fi

chmod 600 "$ENV_FILE" 2>/dev/null || true
prepare_data_dir

APP_PORT="${APP_PORT:-$(get_env_value "$ENV_FILE" "APP_PORT")}"
APP_PORT="${APP_PORT:-3000}"
APP_BIND_HOST="${APP_BIND_HOST:-$(get_env_value "$ENV_FILE" "APP_BIND_HOST")}"
APP_BIND_HOST="${APP_BIND_HOST:-127.0.0.1}"
upsert_env "$ENV_FILE" "APP_PORT" "$APP_PORT"
upsert_env "$ENV_FILE" "APP_BIND_HOST" "$APP_BIND_HOST"
HEALTH_URL="http://127.0.0.1:${APP_PORT}/api/health"

echo "==> Building image"
docker_cmd compose build app

echo "==> Verifying data directory permissions"
ensure_data_dir_writable_for_runtime

echo "==> Starting container"
docker_cmd compose up -d app

echo "==> Waiting for health: $HEALTH_URL"
if ! wait_for_health "$HEALTH_URL" 90 1; then
  echo "Container did not become healthy. Recent logs:" >&2
  docker_cmd compose logs --tail 120 app >&2 || true
  exit 1
fi

echo ""
echo "Docker setup complete."
echo "App URL:"
if [[ "$APP_BIND_HOST" == "0.0.0.0" ]]; then
  echo "  http://<server-ip>:${APP_PORT}"
else
  echo "  http://localhost:${APP_PORT}"
fi
echo ""
echo "Useful commands:"
echo "  docker compose logs -f app"
echo "  docker compose restart app"
echo "  docker compose down"
