#!/usr/bin/env bash
# =====================================================================================
#  Flex sample project – deploy this app on its own domain or subdomain
#
#    sudo ./deploy.sh storynest.example.com
#    sudo ./deploy.sh storynest.example.com --email you@example.com
#
#  What it does (Ubuntu / Debian server):
#    • installs Nginx and PHP 8.3+ (PHP-FPM) if they are missing
#    • installs MySQL/MariaDB if missing, creates a database and user for this app
#    • copies the app to /var/www/<app>, loads the demo data
#    • creates an Nginx site for your domain on the normal web ports (80 / 443)
#    • gets a free HTTPS certificate from Let's Encrypt
#
#  Options:
#    --email EMAIL      email for certificate expiry notices (optional)
#    --db mysql|sqlite  database to use (default: mysql)
#    --no-ssl           skip HTTPS (site stays on http://)
#    --reset-data       wipe the database and reload the demo data
#    --dir PATH         install folder (default: /var/www/<app>)
#
#  Running it again is safe: it updates the code and keeps your data and settings.
# =====================================================================================
set -Eeuo pipefail

HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=/dev/null
source "$HERE/app.conf"   # APP_SLUG, APP_TITLE

DOMAIN=""; EMAIL=""; DB="mysql"; SSL=1; RESET=0; DIR=""
while [ $# -gt 0 ]; do
  case "$1" in
    --email) EMAIL="${2:-}"; shift 2 ;;
    --db) DB="${2:-}"; shift 2 ;;
    --no-ssl) SSL=0; shift ;;
    --reset-data) RESET=1; shift ;;
    --dir) DIR="${2:-}"; shift 2 ;;
    -h|--help) sed -n '2,24p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
    -*) echo "Unknown option: $1 (see ./deploy.sh --help)"; exit 1 ;;
    *) DOMAIN="$1"; shift ;;
  esac
done

# ---------- output helpers ----------
B=$'\e[1m'; G=$'\e[32m'; Y=$'\e[33m'; R=$'\e[31m'; N=$'\e[0m'
step() { echo; echo "${B}==> $*${N}"; }
ok()   { echo "    ${G}✓${N} $*"; }
warn() { echo "    ${Y}!${N} $*"; }
die()  { echo; echo "${R}✗ $*${N}"; exit 1; }

[ "$(id -u)" -eq 0 ] || die "Please run as root:  sudo ./deploy.sh ${DOMAIN:-your.domain.com}"
command -v apt-get >/dev/null || die "This script supports Ubuntu and Debian servers (apt)."
[ -d "$HERE/app" ] || die "The 'app' folder is missing next to deploy.sh. Unzip the full package first."

if [ -z "$DOMAIN" ]; then
  read -rp "Domain for ${APP_TITLE} (e.g. ${APP_SLUG}.yourdomain.com): " DOMAIN
fi
DOMAIN="$(echo "$DOMAIN" | tr '[:upper:]' '[:lower:]' | sed -E 's#^https?://##; s#/.*$##')"
[[ "$DOMAIN" =~ ^[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)+$ ]] || die "'$DOMAIN' is not a valid domain name."
[[ "$DB" =~ ^(mysql|sqlite)$ ]] || die "--db must be 'mysql' or 'sqlite'."
DIR="${DIR:-/var/www/$APP_SLUG}"

LOG="/var/log/flex-deploy-$APP_SLUG.log"
: > "$LOG"
trap 'echo; echo "${R}✗ Something went wrong (line $LINENO). Last lines of $LOG:${N}"; tail -n 25 "$LOG"' ERR
q() { "$@" >>"$LOG" 2>&1; }   # run quietly, keep output in the log

has_systemd() { [ -d /run/systemd/system ]; }
svc_start() {   # start and enable a service; works with and without systemd
  if has_systemd; then systemctl enable --now "$1" >>"$LOG" 2>&1 || systemctl restart "$1" >>"$LOG" 2>&1
  else service "$1" status >/dev/null 2>&1 || service "$1" start >>"$LOG" 2>&1; fi
}
svc_reload() {
  if has_systemd; then systemctl reload "$1" >>"$LOG" 2>&1 || systemctl restart "$1" >>"$LOG" 2>&1
  else service "$1" reload >>"$LOG" 2>&1 || service "$1" start >>"$LOG" 2>&1; fi
}

echo "${B}${APP_TITLE}${N} → ${B}${DOMAIN}${N}   (folder: $DIR, database: $DB)"
export DEBIAN_FRONTEND=noninteractive

# ---------- 1. web server port check ----------
step "Checking the server"
q apt-get update
if command -v ss >/dev/null; then
  PORT80=$(ss -ltnpH 'sport = :80' 2>/dev/null | grep -o 'users:(("[^"]*' | head -1 | cut -d'"' -f2 || true)
  if [ -n "$PORT80" ] && [ "$PORT80" != "nginx" ]; then
    if [ "$PORT80" = "apache2" ] || [ "$PORT80" = "httpd" ]; then
      warn "Apache is already using port 80. Nginx needs that port to serve your domains."
      read -rp "    Stop and disable Apache now? [y/N] " ans
      [[ "$ans" =~ ^[Yy] ]] || die "Stopped. Free port 80 (or remove Apache) and run this script again."
      if has_systemd; then q systemctl disable --now apache2; else q service apache2 stop; fi
      ok "Apache stopped"
    else
      die "Port 80 is used by '$PORT80'. Stop it first, then run this script again."
    fi
  fi
fi
ok "Port 80 is available for Nginx"

# ---------- 2. PHP + Nginx ----------
step "Installing Nginx and PHP"
PHPV=""
if command -v php >/dev/null && php -r 'exit(version_compare(PHP_VERSION, "8.3.0", ">=") ? 0 : 1);'; then
  PHPV="$(php -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')"
elif apt-cache show php8.3-fpm >/dev/null 2>&1; then
  PHPV="8.3"
elif grep -qi '^ID=ubuntu' /etc/os-release; then
  warn "PHP 8.3 is not in this Ubuntu release's sources; adding the ondrej/php repository"
  q apt-get install -y software-properties-common
  q add-apt-repository -y ppa:ondrej/php
  q apt-get update
  PHPV="8.3"
else
  die "PHP 8.3 or newer is required but not available from this server's package sources."
fi
HAS_IPV6=1
[ -f /proc/net/if_inet6 ] || HAS_IPV6=0
fix_ipv6() {   # Nginx's default site listens on IPv6, which fails on servers where IPv6 is disabled
  [ "$HAS_IPV6" -eq 0 ] || return 0
  local f
  for f in /etc/nginx/sites-available/* /etc/nginx/conf.d/*.conf; do
    [ -f "$f" ] && sed -i -E 's/^([[:space:]]*)(listen[[:space:]]+\[::\].*)$/\1# \2  # IPv6 not available on this server/' "$f"
  done
  return 0
}
PKGS=(nginx curl unzip "php$PHPV-fpm" "php$PHPV-cli" "php$PHPV-mysql" "php$PHPV-sqlite3" "php$PHPV-mbstring"
      "php$PHPV-xml" "php$PHPV-curl" "php$PHPV-zip" "php$PHPV-intl" "php$PHPV-bcmath")
if ! q apt-get install -y "${PKGS[@]}"; then
  fix_ipv6            # most common reason the Nginx package fails to finish installing
  q dpkg --configure -a || true
  q apt-get install -y "${PKGS[@]}"
fi
fix_ipv6
PHP_BIN="/usr/bin/php$PHPV"
[ -x "$PHP_BIN" ] || PHP_BIN="$(command -v php)"
svc_start "php$PHPV-fpm"
svc_start nginx
FPM_SOCK="/run/php/php$PHPV-fpm.sock"
ok "Nginx $(nginx -v 2>&1 | grep -o '[0-9][0-9.]*' | head -1) and PHP $("$PHP_BIN" -r 'echo PHP_VERSION;') (FPM)"

# ---------- 3. Database ----------
FRESH=1
[ -f "$DIR/.env" ] && FRESH=0

if [ "$DB" = "mysql" ]; then
  step "Preparing the MySQL database"
  if ! command -v mysqld >/dev/null && ! command -v mariadbd >/dev/null; then
    q apt-get install -y mariadb-server
    ok "MariaDB (MySQL) server installed"
  fi
  if ! mysqladmin ping >/dev/null 2>&1 && ! MYSQL_PWD="${MYSQL_ROOT_PASSWORD:-}" mysqladmin -uroot ping >/dev/null 2>&1; then
    svc_start mariadb 2>/dev/null || svc_start mysql 2>/dev/null || true
    for _ in $(seq 1 15); do mysqladmin ping >/dev/null 2>&1 && break; sleep 1; done
  fi

  mysql_root() {
    if [ -n "${MYSQL_ROOT_PASSWORD:-}" ]; then MYSQL_PWD="$MYSQL_ROOT_PASSWORD" mysql -uroot "$@"; else mysql -uroot "$@"; fi
  }
  if ! mysql_root -e "SELECT 1" >/dev/null 2>&1; then
    read -rsp "    MySQL root password: " MYSQL_ROOT_PASSWORD; echo
    mysql_root -e "SELECT 1" >/dev/null 2>&1 || die "Could not log in to MySQL as root."
  fi

  DB_NAME="flex_${APP_SLUG}"; DB_USER="flex_${APP_SLUG}"
  if [ "$FRESH" -eq 1 ] || ! grep -q '^DB_CONNECTION=mysql' "$DIR/.env"; then
    DB_PASS="$("$PHP_BIN" -r 'echo bin2hex(random_bytes(16));')"
    q mysql_root -e "
      CREATE DATABASE IF NOT EXISTS \`$DB_NAME\` CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
      CREATE USER IF NOT EXISTS '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';
      ALTER USER '$DB_USER'@'localhost' IDENTIFIED BY '$DB_PASS';
      CREATE USER IF NOT EXISTS '$DB_USER'@'127.0.0.1' IDENTIFIED BY '$DB_PASS';
      ALTER USER '$DB_USER'@'127.0.0.1' IDENTIFIED BY '$DB_PASS';
      GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_USER'@'localhost';
      GRANT ALL PRIVILEGES ON \`$DB_NAME\`.* TO '$DB_USER'@'127.0.0.1';
      FLUSH PRIVILEGES;"
    NEW_DB_CREDENTIALS=1
    ok "Database '$DB_NAME' and user '$DB_USER' ready"
  else
    ok "Using the existing database settings in $DIR/.env"
  fi
fi

# ---------- 4. Application files ----------
step "Installing ${APP_TITLE} into $DIR"
mkdir -p "$DIR"
cp -a "$HERE/app/." "$DIR/"
if [ "$FRESH" -eq 1 ]; then
  cp "$DIR/.env.example" "$DIR/.env"
fi

SETENV=("$PHP_BIN" "$HERE/set-env.php" "$DIR/.env")
"${SETENV[@]}" APP_NAME="$APP_TITLE" APP_ENV=production APP_DEBUG=false APP_URL="http://$DOMAIN" LOG_LEVEL=warning
if [ "$DB" = "mysql" ]; then
  if [ "${NEW_DB_CREDENTIALS:-0}" -eq 1 ]; then
    "${SETENV[@]}" DB_CONNECTION=mysql DB_HOST=127.0.0.1 DB_PORT=3306 DB_DATABASE="$DB_NAME" DB_USERNAME="$DB_USER" DB_PASSWORD="$DB_PASS"
  fi
else
  "${SETENV[@]}" DB_CONNECTION=sqlite
  [ -f "$DIR/database/database.sqlite" ] || touch "$DIR/database/database.sqlite"
fi
chmod 640 "$DIR/.env"

# Permissions: the web server (www-data) owns the app; storage and cache must be writable.
chown -R www-data:www-data "$DIR"
find "$DIR" -type d -exec chmod 755 {} +
chmod -R ug+rwX "$DIR/storage" "$DIR/bootstrap/cache" "$DIR/database"

art() { (cd "$DIR" && runuser -u www-data -- "$PHP_BIN" artisan "$@"); }

rm -f "$DIR"/bootstrap/cache/*.php   # drop cached config/routes from a previous run
if ! grep -q '^APP_KEY=base64:' "$DIR/.env"; then q art key:generate --force; fi

if [ "$FRESH" -eq 1 ] || [ "$RESET" -eq 1 ]; then
  q art migrate:fresh --seed --force
  ok "Database created and demo data loaded"
else
  q art migrate --force
  ok "Code updated; your existing data and settings were kept"
fi
q art optimize
ok "App optimised for production"

# ---------- 5. Nginx site ----------
step "Creating the Nginx site for $DOMAIN"
if [ -d /etc/nginx/sites-available ]; then
  SITE="/etc/nginx/sites-available/$DOMAIN.conf"
else
  SITE="/etc/nginx/conf.d/$DOMAIN.conf"
fi
LISTEN6=""
[ "$HAS_IPV6" -eq 1 ] && LISTEN6="    listen [::]:80;"

cat > "$SITE" <<NGINX
# ${APP_TITLE} (Flex sample project) — created by deploy.sh
server {
    listen 80;
${LISTEN6}
    server_name ${DOMAIN};
    root ${DIR}/public;
    index index.php;
    charset utf-8;
    client_max_body_size 10M;

    add_header X-Frame-Options "SAMEORIGIN";
    add_header X-Content-Type-Options "nosniff";

    location / {
        try_files \$uri \$uri/ /index.php?\$query_string;
    }

    location = /favicon.ico { access_log off; log_not_found off; }
    location = /robots.txt  { access_log off; log_not_found off; }

    error_page 404 /index.php;

    location ~ ^/index\.php(/|\$) {
        fastcgi_pass unix:${FPM_SOCK};
        fastcgi_split_path_info ^(.+\.php)(/.*)\$;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
        fastcgi_param DOCUMENT_ROOT \$realpath_root;
        fastcgi_hide_header X-Powered-By;
    }

    location ~ /\.(?!well-known).* {
        deny all;
    }

    access_log /var/log/nginx/${APP_SLUG}.access.log;
    error_log  /var/log/nginx/${APP_SLUG}.error.log;
}
NGINX

if [ -d /etc/nginx/sites-enabled ]; then
  ln -sf "$SITE" "/etc/nginx/sites-enabled/$DOMAIN.conf"
fi
nginx -t >>"$LOG" 2>&1 || die "Nginx rejected the configuration. Details: $LOG"
svc_reload nginx
ok "Site enabled: $SITE"

if command -v ufw >/dev/null && ufw status 2>/dev/null | grep -q "Status: active"; then
  q ufw allow 'Nginx Full'
  ok "Firewall (ufw): ports 80 and 443 allowed"
fi

CODE="$(curl -s -o /dev/null -w '%{http_code}' -H "Host: $DOMAIN" http://127.0.0.1/ || true)"
if [ "$CODE" = "200" ]; then
  ok "The site answers locally (HTTP 200)"
else
  warn "Local test returned HTTP $CODE. Check /var/log/nginx/${APP_SLUG}.error.log and $DIR/storage/logs/"
fi

# ---------- 6. HTTPS ----------
SCHEME="http"
if [ "$SSL" -eq 1 ]; then
  step "Setting up HTTPS (Let's Encrypt)"
  DNS_IPS="$(getent ahostsv4 "$DOMAIN" 2>/dev/null | awk '{print $1}' | sort -u | tr '\n' ' ' || true)"
  PUBLIC_IP="$(curl -4 -s --max-time 6 https://api.ipify.org 2>/dev/null || true)"
  if [ -z "$DNS_IPS" ]; then
    warn "$DOMAIN does not resolve yet. Add a DNS 'A' record pointing to ${PUBLIC_IP:-your server IP},"
    warn "wait a few minutes, then run this script again to add HTTPS."
  else
    if [ -n "$PUBLIC_IP" ] && ! grep -qw "$PUBLIC_IP" <<<"$DNS_IPS"; then
      warn "$DOMAIN points to ${DNS_IPS% } but this server's IP is $PUBLIC_IP. Trying anyway (fine if you use a proxy such as Cloudflare)."
    fi
    q apt-get install -y certbot python3-certbot-nginx
    EMAIL_ARGS=(--register-unsafely-without-email)
    [ -n "$EMAIL" ] && EMAIL_ARGS=(-m "$EMAIL")
    if certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos --redirect --keep-until-expiring "${EMAIL_ARGS[@]}" >>"$LOG" 2>&1; then
      SCHEME="https"
      "${SETENV[@]}" APP_URL="https://$DOMAIN"
      q art config:cache
      ok "HTTPS enabled; http:// now redirects to https://"
    else
      warn "Could not get a certificate (details in $LOG). The site still works on http://."
      warn "Usual causes: DNS not pointing here yet, or port 80 blocked by the hosting provider's firewall."
    fi
  fi
fi

trap - ERR
echo
echo "${G}${B}Done!${N}  ${APP_TITLE} is live at:  ${B}${SCHEME}://${DOMAIN}${N}"
echo
echo "  App folder:     $DIR"
if [ "$DB" = "mysql" ]; then
  echo "  Database:       MySQL '$(grep '^DB_DATABASE=' "$DIR/.env" | cut -d= -f2)' (credentials in $DIR/.env)"
else
  echo "  Database:       SQLite ($DIR/database/database.sqlite)"
fi
echo "  Nginx site:     $SITE"
echo "  Deploy log:     $LOG"
echo
echo "  Change the client name, platform name and colour on the Settings page of the app."
echo "  To reload the demo data:  sudo ./deploy.sh $DOMAIN --reset-data"
