#!/usr/bin/env bash
# ============================================================================
# PROCP — VPS akıllı tek-satır installer (PRC-1136)
#   curl -fsSL https://get.procp.net/install.sh | bash -s -- [flag'ler]
#
# Runtime'ı PİNLER (host'un rastgele PHP'sine güvenmez). İki engine:
#   --engine=docker      (varsayılan) → Compose stack; Meili/Redis/DB dahil.
#   --engine=frankenphp             → gömülü PHP 8.3 tek-binary; Docker'sız VPS.
#
# Flag'ler (hepsi opsiyonel):
#   --engine=docker|frankenphp   çalışma motoru            (vars. docker)
#   --domain=panel.example.com   site alan adı → APP_URL
#   --port=8080                  web portu                 (vars. 80)
#   --target=/opt/procp          kurulum dizini            (vars. ./procp)
#   --meili-host=http://h:7700   harici Meilisearch        (yoksa DEGRADE→database)
#   --meili-key=KEY              Meili master key
#   --redis-host=127.0.0.1       harici Redis              (yoksa DEGRADE→database/file)
#   --redis-port=6379            Redis portu
#   --redis-password=PW          Redis parolası
#   --with-reverb                realtime (chat/ws) servisini de kur
#   --frankenphp-url=URL         FrankenPHP binary override
#   -y | --yes                   soru sorma; varsayılan+flag ile ilerle
#   -h | --help                  bu yardım
#
# İNTERAKTİF MOD: flag'siz (ya da eksik) çalıştırırsan ve terminal varsa,
#   adım adım SİHİRBAZ sorar (motor/domain/port/servis) — predefined varsayılanlarla.
#   Docker zaten kuruluysa yeniden KURMAZ, doğrudan kullanır. -y ile sihirbazı atla.
#
# Env override (flag'e denk): PROCP_BASE, PROCP_TARGET, PROCP_IMAGE,
#   PROCP_FRANKENPHP_URL, FRANKENPHP_VERSION.
#
# Kurulum bitince tarayıcıda  http://<domain>[:port]/install  ile sihirbazı bitir.
#
# NOT — Docker engine harici servisleri compose'tan getirir; --meili-host/
#   --redis-host YALNIZCA --engine=frankenphp'de anlamlıdır.
# NOT — ionCube şu an kapalı (PRC-1133); FrankenPHP kodlanmamış release'e kurulur.
# ============================================================================
set -euo pipefail

BASE="${PROCP_BASE:-https://releases.procp.net}"
ENGINE="docker"
DOMAIN=""
PORT=""
TARGET="${PROCP_TARGET:-$PWD/procp}"
MEILI_HOST=""
MEILI_KEY=""
REDIS_HOST=""
REDIS_PORT="6379"
REDIS_PASSWORD=""
WITH_REVERB="0"
FRANKENPHP_URL="${PROCP_FRANKENPHP_URL:-}"
FRANKENPHP_VERSION="${FRANKENPHP_VERSION:-latest}"
ENGINE_SET=0          # --engine flag'i açıkça verildi mi
ASSUME_YES=0          # -y/--yes → soru sorma, varsayılan+flag ile ilerle
INTERACTIVE=0         # runtime'da TTY + eksik parametre varsa 1

usage() { sed -n '2,42p' "$0" 2>/dev/null || grep '^#' "$0"; }
die()   { echo "✗ $*" >&2; exit 1; }
need()  { command -v "$1" >/dev/null || die "$1 gerekli"; }

# --- flag parse (--key=value ve --flag) ---
for arg in "$@"; do
  case "$arg" in
    --engine=*)         ENGINE="${arg#*=}"; ENGINE_SET=1 ;;
    --domain=*)         DOMAIN="${arg#*=}" ;;
    --port=*)           PORT="${arg#*=}" ;;
    --target=*)         TARGET="${arg#*=}" ;;
    --meili-host=*)     MEILI_HOST="${arg#*=}" ;;
    --meili-key=*)      MEILI_KEY="${arg#*=}" ;;
    --redis-host=*)     REDIS_HOST="${arg#*=}" ;;
    --redis-port=*)     REDIS_PORT="${arg#*=}" ;;
    --redis-password=*) REDIS_PASSWORD="${arg#*=}" ;;
    --with-reverb)      WITH_REVERB="1" ;;
    --frankenphp-url=*) FRANKENPHP_URL="${arg#*=}" ;;
    -y|--yes)           ASSUME_YES=1 ;;
    -h|--help)          usage; exit 0 ;;
    *) die "bilinmeyen argüman: $arg  (yardım: --help)" ;;
  esac
done

[ "$ENGINE" = "docker" ] || [ "$ENGINE" = "frankenphp" ] \
  || die "geçersiz --engine=$ENGINE (docker | frankenphp)"

need curl

# key=value satırını .env'de yerinde değiştir (BSD/GNU sed uyumsuzluğundan kaçın).
set_env() {
  local f="$1" k="$2" v="$3"
  if grep -qE "^$k=" "$f" 2>/dev/null; then
    awk -v k="$k" -v v="$v" 'BEGIN{FS=OFS="="} $1==k{print k"="v; next} {print}' "$f" > "$f.tmp" && mv "$f.tmp" "$f"
  else
    printf '%s=%s\n' "$k" "$v" >> "$f"
  fi
}

# taşınabilir rastgele hex + sha256
rnd()     { openssl rand -hex 16 2>/dev/null || head -c16 /dev/urandom | od -An -tx1 | tr -d ' \n'; }
sha256of(){ if command -v sha256sum >/dev/null; then sha256sum "$1" | cut -d' ' -f1; else shasum -a 256 "$1" | cut -d' ' -f1; fi; }

# --domain + --port → APP_URL (http; TLS sonrası https'e çevrilir)
app_url() {
  local host="${DOMAIN:-localhost}" p="${PORT:-80}" sfx=""
  [ "$p" != "80" ] && sfx=":$p"
  echo "http://${host}${sfx}"
}

# ── İnteraktif yardımcılar (curl|bash'te bile çalışsın diye /dev/tty'den okur;
#    stdin script'in kendisidir, ondan okunamaz) ──
# TTY gerçekten açılıp okunabiliyor mu (var-ama-kullanılamaz /dev/tty'yi ele)
tty_usable() { (exec </dev/tty) 2>/dev/null; }
ask()    { local a=""; printf '%s' "$1" >/dev/tty; IFS= read -r a </dev/tty || a=""; [ -n "$a" ] && printf '%s' "$a" || printf '%s' "$2"; }
ask_yn() { case "$(ask "$1 [$2] " "$2")" in [EeYy]*) return 0 ;; *) return 1 ;; esac; }

# Docker yoksa: interaktifse kurmayı öner (get.docker.com), değilse yönlendir.
# Docker VARSA hiçbir şey yapma — mevcut kurulumu kullan.
ensure_docker() {
  command -v docker >/dev/null 2>&1 && return 0
  if [ "$INTERACTIVE" = "1" ] && ask_yn "Docker kurulu değil — get.docker.com ile kurulsun mu? (root/sudo gerekir)" E; then
    echo "▸ Docker kuruluyor (get.docker.com)"
    curl -fsSL https://get.docker.com | sh || die "Docker kurulumu başarısız"
  else
    die "Docker gerekli — kurun ya da '--engine=frankenphp' kullanın"
  fi
}

# Adım adım sihirbaz — yalnız eksik parametreleri sorar, predefined varsayılanlarla.
run_wizard() {
  { echo ""; echo "════════ PROCP kurulum sihirbazı ════════"; echo "(boş bırak = [köşeli parantez] varsayılanı)"; } >/dev/tty

  # 1) Motor — Docker durumuna göre predefined öneri
  if [ "$ENGINE_SET" = "0" ]; then
    if command -v docker >/dev/null 2>&1; then
      { echo ""; echo "Docker bulundu ✓ (yeniden kurulmayacak)";
        echo "  1) Docker      — önerilen, tüm servisler hazır";
        echo "  2) FrankenPHP  — Docker'sız, gömülü PHP"; } >/dev/tty
      case "$(ask "Motor seç [1]: " 1)" in 2) ENGINE=frankenphp ;; *) ENGINE=docker ;; esac
    else
      { echo ""; echo "Docker bulunamadı.";
        echo "  1) FrankenPHP  — önerilen, Docker'sız gömülü PHP 8.3";
        echo "  2) Docker'ı şimdi kur + kullan"; } >/dev/tty
      case "$(ask "Motor seç [1]: " 1)" in 2) ENGINE=docker ;; *) ENGINE=frankenphp ;; esac
    fi
  fi

  # 2) Domain + 3) Port
  [ -z "$DOMAIN" ] && DOMAIN="$(ask "Alan adı (boş=localhost): " "")"
  [ -z "$PORT" ]   && PORT="$(ask "Web portu [80]: " 80)"

  # 4) FrankenPHP'ye özel: harici servis + realtime
  if [ "$ENGINE" = "frankenphp" ]; then
    if [ -z "$MEILI_HOST$REDIS_HOST" ]; then
      { echo ""; echo "Servisler (boş=DEGRADE → arama/kuyruk database sürücüde):"; } >/dev/tty
      MEILI_HOST="$(ask "  Harici Meilisearch host (boş=degrade): " "")"
      [ -n "$MEILI_HOST" ] && MEILI_KEY="$(ask "  Meili master key: " "")"
      REDIS_HOST="$(ask "  Harici Redis host (boş=degrade): " "")"
    fi
    [ "$WITH_REVERB" = "0" ] && { ask_yn "Realtime (canlı chat) servisi kurulsun mu?" h && WITH_REVERB=1; }
  fi

  # 5) Özet + onay
  { echo ""; echo "── Özet ──";
    echo "  motor : $ENGINE";
    echo "  URL   : $(app_url)";
    echo "  hedef : $TARGET"; } >/dev/tty
  [ "$ENGINE" = "frankenphp" ] && \
    echo "  servis: arama=$([ -n "$MEILI_HOST" ] && echo Meili || echo db-degrade) kuyruk=$([ -n "$REDIS_HOST" ] && echo Redis || echo db-degrade) reverb=$([ "$WITH_REVERB" = "1" ] && echo evet || echo hayır)" >/dev/tty
  ask_yn "Kuruluma başlayalım mı?" E || { echo "iptal edildi." >/dev/tty; exit 0; }
}

# ════════════════════════════════════════════════════════════════════════════
# ENGINE: docker — kanıtlı install-docker.sh'e delege (flag'ler → env)
# ════════════════════════════════════════════════════════════════════════════
engine_docker() {
  ensure_docker   # Docker varsa dokunmaz; yoksa interaktifse kurmayı önerir
  [ -z "$MEILI_HOST$REDIS_HOST" ] || echo "ⓘ --meili-host/--redis-host docker engine'de yok sayılır (compose sağlar)"
  export PROCP_BASE PROCP_TARGET="$TARGET"
  [ -n "$PORT" ]   && export APP_PORT="$PORT"
  [ -n "$DOMAIN" ] && export PROCP_DOMAIN="$DOMAIN"

  local sib; sib="$(cd "$(dirname "$0")" 2>/dev/null && pwd)/install-docker.sh"
  if [ -f "$sib" ]; then
    echo "▸ Docker engine → $sib"
    bash "$sib"
  else
    echo "▸ Docker engine → $BASE/install-docker.sh"
    curl -fsSL "$BASE/install-docker.sh" | bash
  fi
}

# ════════════════════════════════════════════════════════════════════════════
# ENGINE: frankenphp — gömülü PHP 8.3 tek-binary (Docker'sız)
# ════════════════════════════════════════════════════════════════════════════
engine_frankenphp() {
  need tar
  local APPDIR="$TARGET/app" BINDIR="$TARGET/bin"
  local p="${PORT:-80}"
  mkdir -p "$APPDIR" "$BINDIR"

  # 1) FrankenPHP binary (os/arch)
  local s m fos farch
  s="$(uname -s)"; m="$(uname -m)"
  case "$s" in Linux) fos="linux" ;; Darwin) fos="mac" ;; *) die "desteklenmeyen OS: $s" ;; esac
  case "$m" in
    x86_64|amd64)  farch="x86_64" ;;
    aarch64|arm64) [ "$fos" = "mac" ] && farch="arm64" || farch="aarch64" ;;
    *) die "desteklenmeyen mimari: $m" ;;
  esac
  local url="$FRANKENPHP_URL"
  if [ -z "$url" ]; then
    url="https://github.com/dunglas/frankenphp/releases/${FRANKENPHP_VERSION}/download/frankenphp-${fos}-${farch}"
    [ "$FRANKENPHP_VERSION" = "latest" ] && url="https://github.com/dunglas/frankenphp/releases/latest/download/frankenphp-${fos}-${farch}"
  fi
  echo "▸ FrankenPHP indiriliyor ($fos-$farch)"
  # Önce kendi mirror'ımız ($BASE/bin), sonra resmi kaynak
  curl -fsSL "$BASE/bin/frankenphp-${fos}-${farch}" -o "$BINDIR/frankenphp" 2>/dev/null \
    || curl -fSL "$url" -o "$BINDIR/frankenphp"
  chmod +x "$BINDIR/frankenphp"
  "$BINDIR/frankenphp" version >/dev/null 2>&1 || die "FrankenPHP binary çalışmıyor"

  # 2) Release arşivi (latest.json → indir + checksum)
  echo "▸ sürüm bilgisi alınıyor"
  local meta ver sha; meta="$(curl -fsSL "$BASE/latest.json")"
  ver="$(printf '%s' "$meta" | grep -oE '"version"[^,]*'        | head -1 | cut -d'"' -f4)"
  sha="$(printf '%s' "$meta" | grep -oE '"tarball_sha256"[^,]*' | head -1 | cut -d'"' -f4)"
  [ -n "$ver" ] || die "sürüm okunamadı"
  echo "▸ procp-$ver indiriliyor"
  local tmp; tmp="$(mktemp -d)"; curl -fsSL "$BASE/procp-$ver.tar.gz" -o "$tmp/procp.tgz"
  if [ -n "$sha" ]; then
    echo "▸ checksum doğrulanıyor"
    [ "$(sha256of "$tmp/procp.tgz")" = "$sha" ] || { rm -rf "$tmp"; die "checksum uyuşmadı"; }
  fi
  echo "▸ açılıyor → $APPDIR"
  tar -C "$APPDIR" -xzf "$tmp/procp.tgz"; rm -rf "$tmp"

  # 3) .env (arşivden .env.example, yoksa $BASE)
  local env="$APPDIR/.env"
  if [ ! -f "$env" ]; then
    [ -f "$APPDIR/.env.example" ] || curl -fsSL "$BASE/.env.example" -o "$APPDIR/.env.example"
    cp "$APPDIR/.env.example" "$env"
    echo "▸ .env üretiliyor"
    set_env "$env" APP_ENV       production
    set_env "$env" APP_DEBUG     false
    set_env "$env" APP_KEY       "base64:$(openssl rand -base64 32 2>/dev/null || head -c32 /dev/urandom | base64)"
    set_env "$env" APP_URL       "$(app_url)"
    set_env "$env" DB_HOST       "127.0.0.1"   # DB /install sihirbazında girilir

    # --- Redis: verilmişse kullan, yoksa DEGRADE (file/database) ---
    if [ -n "$REDIS_HOST" ]; then
      set_env "$env" REDIS_HOST       "$REDIS_HOST"
      set_env "$env" REDIS_PORT       "$REDIS_PORT"
      set_env "$env" REDIS_PASSWORD   "${REDIS_PASSWORD:-null}"
      set_env "$env" CACHE_STORE      redis
      set_env "$env" CACHE_DRIVER     redis
      set_env "$env" SESSION_DRIVER   redis
      set_env "$env" QUEUE_CONNECTION redis
    else
      set_env "$env" CACHE_STORE      file
      set_env "$env" CACHE_DRIVER     file
      set_env "$env" SESSION_DRIVER   file
      set_env "$env" QUEUE_CONNECTION database
    fi

    # --- Meili: verilmişse kullan, yoksa DEGRADE (database arama) ---
    if [ -n "$MEILI_HOST" ]; then
      set_env "$env" SCOUT_DRIVER     meilisearch
      set_env "$env" MEILISEARCH_HOST "$MEILI_HOST"
      set_env "$env" MEILISEARCH_KEY  "$MEILI_KEY"
    else
      set_env "$env" SCOUT_DRIVER     database
    fi

    # --- Reverb (realtime): opsiyonel ---
    if [ "$WITH_REVERB" = "1" ]; then
      set_env "$env" BROADCAST_DRIVER   reverb
      set_env "$env" REVERB_APP_ID      procp
      set_env "$env" REVERB_APP_KEY     "$(rnd)"
      set_env "$env" REVERB_APP_SECRET  "$(rnd)"
    else
      set_env "$env" BROADCAST_DRIVER   log
    fi
  else
    echo "▸ .env zaten var — korunuyor"
  fi

  chmod -R u+rwX "$APPDIR/storage" "$APPDIR/bootstrap/cache" 2>/dev/null || true

  # 4) Servisler — web + worker + scheduler (+reverb). systemd varsa unit,
  #    yoksa nohup komut seti yazdır (queue+cron olmadan mail/provizyon durur).
  setup_services "$APPDIR" "$BINDIR/frankenphp" "$p"

  local host="${DOMAIN:-localhost}" sfx=""; [ "$p" != "80" ] && sfx=":$p"
  cat <<EOF

✓ PROCP (FrankenPHP) yerleştirildi: $APPDIR
  Kurulumu tamamla →  http://${host}${sfx}/install
  Arama/kuyruk: $([ -n "$MEILI_HOST" ] && echo "Meili" || echo "database (degrade)") / $([ -n "$REDIS_HOST" ] && echo "Redis" || echo "database (degrade)")
EOF
}

# systemd unit'leri (root) ya da nohup fallback
setup_services() {
  local appdir="$1" fbin="$2" port="$3"
  local web="$fbin php-server --root $appdir/public --listen :$port"
  local worker="$fbin php-cli $appdir/artisan queue:work database --sleep=3 --tries=3 --max-time=3600 --backoff=10"
  local sched="$fbin php-cli $appdir/artisan schedule:work"
  local reverb="$fbin php-cli $appdir/artisan reverb:start --host=0.0.0.0 --port=8080"

  if command -v systemctl >/dev/null && [ "$(id -u)" = "0" ]; then
    echo "▸ systemd servisleri kuruluyor"
    _unit procp-web      "PROCP web (FrankenPHP)"       "$web"    "$appdir"
    _unit procp-worker   "PROCP queue worker"           "$worker" "$appdir"
    _unit procp-scheduler "PROCP scheduler"             "$sched"  "$appdir"
    local units="procp-web procp-worker procp-scheduler"
    if [ "$WITH_REVERB" = "1" ]; then
      _unit procp-reverb "PROCP reverb (realtime)"      "$reverb" "$appdir"
      units="$units procp-reverb"
    fi
    systemctl daemon-reload
    # shellcheck disable=SC2086
    systemctl enable --now $units
    echo "  durum:  systemctl status procp-web"
  else
    cat <<EOF

⚠ systemd yok veya root değilsin — servisler ELLE başlatılmalı
  (queue+cron çalışmazsa mail/provizyon/billing DURUR):

  # web
  nohup $web > $appdir/storage/logs/web.log 2>&1 &
  # queue worker
  nohup $worker > $appdir/storage/logs/worker.log 2>&1 &
  # scheduler (cron)
  nohup $sched > $appdir/storage/logs/scheduler.log 2>&1 &$([ "$WITH_REVERB" = "1" ] && printf '\n  # reverb\n  nohup %s > %s/storage/logs/reverb.log 2>&1 &' "$reverb" "$appdir")
EOF
  fi
}

_unit() {
  local name="$1" desc="$2" cmd="$3" wd="$4"
  cat > "/etc/systemd/system/$name.service" <<EOF
[Unit]
Description=$desc
After=network.target

[Service]
Type=simple
WorkingDirectory=$wd
ExecStart=$cmd
Restart=always
RestartSec=5

[Install]
WantedBy=multi-user.target
EOF
}

# İnteraktif mi? -y yoksa, TTY GERÇEKTEN kullanılabiliyorsa ve zorunlulardan
# biri (motor/domain/port) eksikse adım adım sihirbaz.
if [ "$ASSUME_YES" != "1" ] && tty_usable \
   && { [ "$ENGINE_SET" = "0" ] || [ -z "$DOMAIN" ] || [ -z "$PORT" ]; }; then
  INTERACTIVE=1
  run_wizard
fi

echo "▸ PROCP installer — engine=$ENGINE, target=$TARGET${DOMAIN:+, domain=$DOMAIN}${PORT:+, port=$PORT}"
case "$ENGINE" in
  docker)     engine_docker ;;
  frankenphp) engine_frankenphp ;;
esac
