#!/usr/bin/env bash
# YakPanel (YakPanel-server) — native Linux installer (systemd + Nginx + Redis).
#
# One-liners (run as root):
#   curl -fsSL https://www.yakpanel.com/YakPanel-server/install.sh | bash
#   wget -qO- https://www.yakpanel.com/YakPanel-server/install.sh | bash
#
# Preserve environment through sudo:
#   sudo -E bash install.sh
#
# Environment (optional):
#   REPO_URL              Git URL (default: https://source.yakpanel.com/admin/yakpanel-core.git)
#   YAKPANEL_BRANCH       Branch/tag for shallow clone (default: default branch)
#   GIT_REF               Alias for YAKPANEL_BRANCH
#   INSTALL_PATH          Install dir (default: /www/server/YakPanel-server)
#   PANEL_PORT            Nginx listen port (default: 8888)
#   BACKEND_PORT          Uvicorn port (default: 8889)
#   YAKPANEL_SOURCE_DIR   If set, skip git: must contain backend/ and frontend/ (air-gap / local tree)
#
# From an already-cloned YakPanel-server repo:
#   sudo YAKPANEL_SOURCE_DIR="$(pwd)" bash install.sh

set -euo pipefail

REPO_URL="${REPO_URL:-https://source.yakpanel.com/admin/yakpanel-core.git}"
INSTALL_PATH="${INSTALL_PATH:-/www/server/YakPanel-server}"
PANEL_PORT="${PANEL_PORT:-8888}"
BACKEND_PORT="${BACKEND_PORT:-8889}"
YAKPANEL_BRANCH="${YAKPANEL_BRANCH:-${GIT_REF:-}}"
SYSTEMD_UNIT="YakPanel-server"

while [ $# -gt 0 ]; do
  case "$1" in
    --repo-url)
      REPO_URL="$2"
      shift 2
      ;;
    --install-path)
      INSTALL_PATH="$2"
      shift 2
      ;;
    --branch|--ref)
      YAKPANEL_BRANCH="$2"
      shift 2
      ;;
    --source-dir)
      YAKPANEL_SOURCE_DIR="$2"
      shift 2
      ;;
    --panel-port)
      PANEL_PORT="$2"
      shift 2
      ;;
    --backend-port)
      BACKEND_PORT="$2"
      shift 2
      ;;
    -h|--help)
      grep '^#' "$0" | grep -v '^#!/' | sed 's/^# \{0,1\}//'
      exit 0
      ;;
    *)
      echo "Unknown option: $1 (use --help)"
      exit 1
      ;;
  esac
done

echo "=========================================="
echo "  YakPanel Installer (YakPanel-server)"
echo "=========================================="

if [ "${EUID:-$(id -u)}" -ne 0 ]; then
  echo "Run as root, e.g.: sudo -E bash $0"
  exit 1
fi

detect_pkg_manager() {
  if command -v apt-get >/dev/null 2>&1; then
    echo "apt"
  elif command -v dnf >/dev/null 2>&1; then
    echo "dnf"
  elif command -v yum >/dev/null 2>&1; then
    echo "yum"
  else
    echo ""
  fi
}

PKG=$(detect_pkg_manager)
if [ -z "$PKG" ]; then
  echo "No supported package manager (apt-get, dnf, or yum). Use Docker instead; see README.md."
  exit 1
fi

install_base_packages() {
  echo ""
  echo "[1/6] Installing system dependencies ($PKG)..."
  case "$PKG" in
    apt)
      apt-get update -qq
      DEBIAN_FRONTEND=noninteractive apt-get install -y \
        git python3 python3-venv python3-pip python3-dev build-essential \
        redis-server nginx curl ca-certificates
      ;;
    dnf|yum)
      if [ "$PKG" = dnf ]; then
        $PKG install -y dnf-plugins-core 2>/dev/null || true
      fi
      if [ "$PKG" = dnf ] && [ -f /etc/almalinux-release ]; then
        dnf config-manager --set-enabled crb 2>/dev/null || dnf config-manager --set-enabled powertools 2>/dev/null || true
      fi
      if [ "$PKG" = dnf ] && [ -f /etc/rocky-release ]; then
        dnf config-manager --set-enabled crb 2>/dev/null || true
      fi
      $PKG install -y git python3 python3-pip nginx redis curl ca-certificates \
        gcc-c++ make python3-devel openssl-devel || \
      $PKG install -y git python3 python3-pip nginx redis curl ca-certificates gcc-c++ make
      ;;
  esac
}

# Default dist Python (3.10+ on Ubuntu 22.04) is enough; otherwise install 3.11 from apt.
ensure_python_for_backend() {
  if [ -n "${PYTHON_CMD:-}" ]; then
    return 0
  fi
  if python3 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)' 2>/dev/null; then
    PYTHON_CMD=python3
    return 0
  fi
  if command -v python3.11 >/dev/null 2>&1 && python3.11 -c 'import sys; raise SystemExit(0 if sys.version_info >= (3, 10) else 1)' 2>/dev/null; then
    PYTHON_CMD=python3.11
    return 0
  fi
  if [ "$PKG" = apt ]; then
    echo "Python 3.10+ required for the backend; installing python3.11 from apt..."
    DEBIAN_FRONTEND=noninteractive apt-get install -y python3.11 python3.11-venv python3.11-dev || {
      echo "Could not install python3.11. Enable universe or install Python 3.10+ manually."
      exit 1
    }
    PYTHON_CMD=python3.11
    return 0
  fi
  echo "Python 3.10+ required. Current: $(python3 -V 2>/dev/null || echo 'python3 missing')"
  exit 1
}

ensure_node_18_plus() {
  local major
  if command -v node >/dev/null 2>&1; then
    major="$(node -v | sed 's/^v//' | cut -d. -f1)"
    if [ "${major:-0}" -ge 18 ] 2>/dev/null; then
      return 0
    fi
  fi
  echo "Installing Node.js 20 (NodeSource)..."
  case "$PKG" in
    apt)
      curl -fsSL https://deb.nodesource.com/setup_20.x | bash -
      DEBIAN_FRONTEND=noninteractive apt-get install -y nodejs
      ;;
    dnf|yum)
      curl -fsSL https://rpm.nodesource.com/setup_20.x | bash -
      $PKG install -y nodejs
      ;;
  esac
}

find_yakpanel_root_in_tree() {
  local root="$1"
  if [ -d "$root/YakPanel-server/backend" ] && [ -d "$root/YakPanel-server/frontend" ]; then
    echo "$root/YakPanel-server"
  elif [ -d "$root/YakPanel-master/YakPanel-server/backend" ] && [ -d "$root/YakPanel-master/YakPanel-server/frontend" ]; then
    echo "$root/YakPanel-master/YakPanel-server"
  elif [ -d "$root/yakpanel-server/backend" ] && [ -d "$root/yakpanel-server/frontend" ]; then
    echo "$root/yakpanel-server"
  elif [ -d "$root/YakPanel-master/yakpanel-server/backend" ] && [ -d "$root/YakPanel-master/yakpanel-server/frontend" ]; then
    echo "$root/YakPanel-master/yakpanel-server"
  elif [ -d "$root/backend" ] && [ -d "$root/frontend" ]; then
    echo "$root"
  else
    echo ""
  fi
}

stage_source_to_install_path() {
  local src="$1"
  mkdir -p "$(dirname "$INSTALL_PATH")"
  rm -rf "$INSTALL_PATH"
  cp -a "$src" "$INSTALL_PATH"
}

install_base_packages
ensure_python_for_backend
ensure_node_18_plus

echo ""
echo "[2/6] Obtaining YakPanel source..."
if [ -n "${YAKPANEL_SOURCE_DIR:-}" ]; then
  if [ ! -d "$YAKPANEL_SOURCE_DIR/backend" ] || [ ! -d "$YAKPANEL_SOURCE_DIR/frontend" ]; then
    echo "YAKPANEL_SOURCE_DIR must contain backend/ and frontend/: $YAKPANEL_SOURCE_DIR"
    exit 1
  fi
  stage_source_to_install_path "$YAKPANEL_SOURCE_DIR"
else
  TMP_DIR="$(mktemp -d)"
  trap 'rm -rf "$TMP_DIR"' EXIT
  export GIT_TERMINAL_PROMPT=0
  # Disable credential helpers for this clone so anonymous HTTPS public repos work
  # from curl|bash (no TTY); USER:TOKEN in REPO_URL is still used if present.
  CLONE_ARGS=(git -c credential.helper= clone --depth 1)
  if [ -n "$YAKPANEL_BRANCH" ]; then
    CLONE_ARGS+=(--branch "$YAKPANEL_BRANCH")
  fi
  CLONE_ARGS+=("$REPO_URL" "$TMP_DIR/repo")
  if ! "${CLONE_ARGS[@]}"; then
    echo "Git clone failed. Check REPO_URL=$REPO_URL, DNS, and outbound HTTPS."
    echo "If the server requires sign-in: allow anonymous clone for HTTP(S), or use a token in the URL, e.g."
    echo "  REPO_URL=https://USER:TOKEN@source.yakpanel.com/admin/yakpanel-core.git"
    echo "Or override: REPO_URL=https://your.other.git/mirror.git"
    exit 1
  fi
  SRC_DIR="$(find_yakpanel_root_in_tree "$TMP_DIR/repo")"
  if [ -z "$SRC_DIR" ]; then
    echo "Could not find YakPanel-server layout (backend/ + frontend/) under cloned repo."
    exit 1
  fi
  stage_source_to_install_path "$SRC_DIR"
  trap - EXIT
  rm -rf "$TMP_DIR"
fi

echo ""
echo "[3/6] Setting up backend..."
cd "$INSTALL_PATH/backend"
"$PYTHON_CMD" -m venv venv
# shellcheck source=/dev/null
source venv/bin/activate
pip install -q -r requirements.txt
./venv/bin/python -c "import app.main" || {
  echo "ERROR: Backend failed to import (see traceback above). Check Python version and requirements."
  exit 1
}
python scripts/seed_admin.py
deactivate

echo ""
echo "[4/6] Building frontend..."
cd "$INSTALL_PATH/frontend"
npm install --silent
# npm life-cycle scripts often shell-exec node_modules/.bin/* — fails on noexec or missing +x.
chmod +x node_modules/.bin/* 2>/dev/null || true
npm run build

echo ""
echo "[5/6] Configuring systemd..."
REDIS_AFTER="redis.target"
REDIS_WANTS=""
case "$PKG" in
  apt)
    REDIS_AFTER="redis-server.service"
    REDIS_WANTS="Wants=redis-server.service"
    ;;
  dnf|yum)
    REDIS_AFTER="redis.service"
    REDIS_WANTS="Wants=redis.service"
    ;;
esac

# Redis must be up before Uvicorn; starting it after the panel caused 502 (nginx OK, API down).
case "$PKG" in
  apt)
    systemctl enable redis-server 2>/dev/null || true
    systemctl start redis-server 2>/dev/null || true
    ;;
  dnf|yum)
    systemctl enable redis 2>/dev/null || true
    systemctl start redis 2>/dev/null || true
    ;;
esac

cat > "/etc/systemd/system/${SYSTEMD_UNIT}.service" << EOF
[Unit]
Description=YakPanel Backend
After=network.target $REDIS_AFTER
$REDIS_WANTS

[Service]
Type=simple
User=root
WorkingDirectory=$INSTALL_PATH/backend
Environment="PATH=$INSTALL_PATH/backend/venv/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
Environment=PYTHONUNBUFFERED=1
ExecStart=$INSTALL_PATH/backend/venv/bin/uvicorn app.main:app --host 127.0.0.1 --port $BACKEND_PORT
Restart=always
RestartSec=3

[Install]
WantedBy=multi-user.target
EOF

systemctl daemon-reload
systemctl enable "$SYSTEMD_UNIT"
systemctl restart "$SYSTEMD_UNIT" || systemctl start "$SYSTEMD_UNIT"

backend_ok=0
for _ in {1..120}; do
  if curl -sfS --max-time 2 "http://127.0.0.1:${BACKEND_PORT}/api/health" >/dev/null 2>&1; then
    backend_ok=1
    break
  fi
  sleep 0.5
done
if [ "$backend_ok" -ne 1 ]; then
  echo ""
  echo "WARNING: Backend did not respond on http://127.0.0.1:${BACKEND_PORT}/api/health (login will show 502 via Nginx)."
  echo "Check: systemctl status $SYSTEMD_UNIT --no-pager"
  echo "Logs:  journalctl -u $SYSTEMD_UNIT -n 80 --no-pager"
  echo "If SELinux is Enforcing: setsebool -P httpd_can_network_connect 1"
fi

write_nginx_site() {
  local target="$1"
  cat > "$target" << NGINXEOF
server {
    listen $PANEL_PORT;
    server_name _;
    root $INSTALL_PATH/frontend/dist;
    index index.html;
    location / {
        try_files \$uri \$uri/ /index.html;
    }
    location /api {
        proxy_pass http://127.0.0.1:$BACKEND_PORT;
        proxy_http_version 1.1;
        proxy_set_header Host \$host;
        proxy_set_header X-Real-IP \$remote_addr;
        proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto \$scheme;
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}
NGINXEOF
}

echo ""
echo "[6/6] Configuring Nginx..."
if [ -d /etc/nginx/sites-available ] && [ -d /etc/nginx/sites-enabled ]; then
  write_nginx_site /etc/nginx/sites-available/YakPanel-server
  ln -sf /etc/nginx/sites-available/YakPanel-server /etc/nginx/sites-enabled/YakPanel-server
else
  write_nginx_site /etc/nginx/conf.d/YakPanel-server.conf
fi

systemctl enable nginx 2>/dev/null || true
systemctl start nginx 2>/dev/null || true
nginx -t
systemctl reload nginx 2>/dev/null || systemctl restart nginx

# Nginx -> upstream (e.g. 127.0.0.1:$BACKEND_PORT) is blocked by default on EL + SELinux => 502.
if command -v getenforce >/dev/null 2>&1 && [ "$(getenforce 2>/dev/null)" = "Enforcing" ]; then
  if command -v setsebool >/dev/null 2>&1; then
    setsebool -P httpd_can_network_connect 1 2>/dev/null || true
  fi
fi

if systemctl is-active --quiet firewalld 2>/dev/null; then
  firewall-cmd --permanent --add-port="${PANEL_PORT}/tcp" >/dev/null 2>&1 || true
  firewall-cmd --reload >/dev/null 2>&1 || true
fi

if command -v ufw >/dev/null 2>&1; then
  ufw allow "${PANEL_PORT}/tcp" 2>/dev/null || true
fi

echo ""
echo "=========================================="
echo "  YakPanel installed successfully!"
echo "=========================================="
echo ""
echo "  Access: http://YOUR_SERVER_IP:$PANEL_PORT"
echo "  Login:  admin / admin"
echo ""
echo "  Change your password after first login."
echo "  UI works but login shows 502? Backend down or SELinux: systemctl status $SYSTEMD_UNIT; journalctl -u $SYSTEMD_UNIT -n 50"
echo "  SELinux: installer calls setsebool httpd_can_network_connect if Enforcing; see README for 403 on static files."
echo "=========================================="
