#!/usr/bin/env bash
#
# Taskoro server installer
# Run from the project directory:
#   bash setup.sh
#
# The script creates backend/.env, installs dependencies, builds the application,
# and can create/start a systemd service when systemd is available.
#
set -Eeuo pipefail

ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ENV_FILE="$ROOT_DIR/backend/.env"
TEMP_ENV="$(mktemp)"
KEEP_TEMP_ENV=1

cleanup() {
  if [[ "$KEEP_TEMP_ENV" -eq 1 ]]; then
    rm -f "$TEMP_ENV"
  fi
}
trap cleanup EXIT

red() { printf '\033[0;31m%s\033[0m\n' "$*"; }
green() { printf '\033[0;32m%s\033[0m\n' "$*"; }
yellow() { printf '\033[0;33m%s\033[0m\n' "$*"; }
blue() { printf '\033[0;34m%s\033[0m\n' "$*"; }

prompt() {
  local __resultvar="$1"
  local label="$2"
  local default="${3-}"
  local value

  if [[ -n "$default" ]]; then
    read -r -p "$label [$default]: " value
    value="${value:-$default}"
  else
    read -r -p "$label: " value
  fi
  printf -v "$__resultvar" '%s' "$value"
}

prompt_required() {
  local __resultvar="$1"
  local label="$2"
  local default="${3-}"
  local prompted
  while true; do
    prompt prompted "$label" "$default"
    if [[ -n "$prompted" ]]; then
      printf -v "$__resultvar" '%s' "$prompted"
      return
    fi
    yellow "This value is required."
  done
}

prompt_secret() {
  local __resultvar="$1"
  local label="$2"
  local value
  read -r -s -p "$label: " value
  printf '\n'
  printf -v "$__resultvar" '%s' "$value"
}

prompt_yes_no() {
  local __resultvar="$1"
  local label="$2"
  local default="${3:-y}"
  local value
  while true; do
    read -r -p "$label [${default^^}/$([[ "$default" == "y" ]] && echo N || echo Y)]: " value
    value="${value:-$default}"
    case "${value,,}" in
      y|yes) printf -v "$__resultvar" 'yes'; return ;;
      n|no) printf -v "$__resultvar" 'no'; return ;;
      *) yellow "Please answer yes or no." ;;
    esac
  done
}

dotenv_quote() {
  local value="$1"
  value="${value//\\/\\\\}"
  value="${value//\"/\\\"}"
  value="${value//$'\n'/\\n}"
  printf '"%s"' "$value"
}

write_env() {
  printf '%s=%s\n' "$1" "$(dotenv_quote "$2")" >> "$TEMP_ENV"
}

generate_secret() {
  node -e "console.log(require('crypto').randomBytes(48).toString('hex'))"
}

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

if [[ ! -f "$ROOT_DIR/backend/package.json" || ! -f "$ROOT_DIR/frontend/package.json" ]]; then
  red "Run this script from a complete Taskoro project directory."
  exit 1
fi

require_command node
require_command npm

NODE_MAJOR="$(node -p "process.versions.node.split('.')[0]")"
if (( NODE_MAJOR < 20 )); then
  red "Node.js 20 or newer is required. Detected: $(node --version)"
  exit 1
fi

blue "=========================================="
blue "       Taskoro server installation"
blue "=========================================="
printf '\n'
printf '%s\n' "This wizard is in English and writes configuration to:"
printf '  %s\n\n' "$ENV_FILE"
yellow "Secrets are entered without being displayed and the .env file is restricted to the current user."
printf '\n'

if [[ -f "$ENV_FILE" ]]; then
  yellow "An existing backend/.env file was found."
  prompt_yes_no REPLACE_ENV "Replace it with a new configuration?" "n"
  if [[ "$REPLACE_ENV" != "yes" ]]; then
    red "Installation cancelled. Existing configuration was not changed."
    exit 1
  fi
  BACKUP_FILE="${ENV_FILE}.backup.$(date +%Y%m%d-%H%M%S)"
  cp "$ENV_FILE" "$BACKUP_FILE"
  green "Existing configuration backed up to $BACKUP_FILE"
fi

prompt NODE_ENV "Environment (production is recommended)" "production"
if [[ "$NODE_ENV" != "production" ]]; then
  yellow "Non-production mode selected. Production-only checks will be skipped."
fi

prompt_required APP_BASE_URL "Public backend/application URL" "http://localhost:4000"
prompt FRONTEND_URL "Public frontend URL (use the same URL for the built-in single-port setup)" "$APP_BASE_URL"
prompt PORT "Application port" "4000"
prompt HOST "Application host" "0.0.0.0"

printf '\n'
blue "Database configuration"
printf '%s\n' "You can provide a complete MySQL URL or use separate connection fields."
prompt_secret DATABASE_URL "MySQL connection URL (leave blank to enter separate fields)"

DB_HOST=""
DB_USER=""
DB_PASSWORD=""
DB_NAME=""
DB_PORT=""
if [[ -z "$DATABASE_URL" ]]; then
  prompt_required DB_HOST "MySQL host" "127.0.0.1"
  prompt_required DB_USER "MySQL user" "taskoro"
  prompt_secret DB_PASSWORD "MySQL password"
  prompt_required DB_NAME "MySQL database name" "taskoro"
  prompt DB_PORT "MySQL port" "3306"
fi

printf '\n'
blue "Application security"
prompt_yes_no GENERATE_JWT "Generate a secure JWT secret automatically?" "y"
if [[ "$GENERATE_JWT" == "yes" ]]; then
  JWT_SECRET="$(generate_secret)"
else
  prompt_secret JWT_SECRET "JWT secret"
fi
prompt_yes_no GENERATE_SESSION "Generate a separate session/signing secret automatically?" "y"
if [[ "$GENERATE_SESSION" == "yes" ]]; then
  SESSION_SECRET="$(generate_secret)"
else
  prompt_secret SESSION_SECRET "Session/signing secret"
fi

printf '\n'
blue "SMTP configuration"
if [[ "$NODE_ENV" == "production" ]]; then
  yellow "SMTP is required in production because account verification emails must be delivered."
  prompt_required SMTP_HOST "SMTP host"
else
  prompt SMTP_HOST "SMTP host (leave blank to disable email delivery in development)" ""
fi
SMTP_PORT="587"
SMTP_ENCRYPTION="tls"
SMTP_SECURE="false"
SMTP_FROM_ADDRESS=""
SMTP_FROM_NAME="Taskoro"
SMTP_USER=""
SMTP_PASS=""
if [[ -n "$SMTP_HOST" ]]; then
  prompt SMTP_PORT "SMTP port" "587"
  prompt SMTP_ENCRYPTION "SMTP encryption (tls or ssl)" "tls"
  [[ "$SMTP_ENCRYPTION" == "ssl" ]] && SMTP_SECURE="true"
  prompt_required SMTP_FROM_ADDRESS "From email address"
  prompt SMTP_FROM_NAME "From display name" "Taskoro"
  prompt SMTP_USER "SMTP username (leave blank if not required)" ""
  if [[ -n "$SMTP_USER" ]]; then
    prompt_secret SMTP_PASS "SMTP password"
  fi
elif [[ "$NODE_ENV" == "production" ]]; then
  red "SMTP_HOST is required in production."
  exit 1
fi

printf '\n'
blue "Optional integrations"
printf '%s\n' "Leave an integration disabled if you do not use it. You can edit backend/.env later."

SLACK_CLIENT_ID=""
SLACK_CLIENT_SECRET=""
prompt_yes_no ENABLE_SLACK "Configure Slack OAuth?" "n"
if [[ "$ENABLE_SLACK" == "yes" ]]; then
  prompt_required SLACK_CLIENT_ID "Slack client ID"
  prompt_secret SLACK_CLIENT_SECRET "Slack client secret"
fi

ZOOM_CLIENT_ID=""
ZOOM_CLIENT_SECRET=""
prompt_yes_no ENABLE_ZOOM "Configure Zoom OAuth?" "n"
if [[ "$ENABLE_ZOOM" == "yes" ]]; then
  prompt_required ZOOM_CLIENT_ID "Zoom client ID"
  prompt_secret ZOOM_CLIENT_SECRET "Zoom client secret"
fi

GOOGLE_CLIENT_ID=""
GOOGLE_CLIENT_SECRET=""
prompt_yes_no ENABLE_GOOGLE "Configure Google Calendar OAuth?" "n"
if [[ "$ENABLE_GOOGLE" == "yes" ]]; then
  prompt_required GOOGLE_CLIENT_ID "Google OAuth client ID"
  prompt_secret GOOGLE_CLIENT_SECRET "Google OAuth client secret"
fi

STRIPE_SECRET_KEY=""
STRIPE_WEBHOOK_SECRET=""
prompt_yes_no ENABLE_STRIPE "Configure Stripe payments?" "n"
if [[ "$ENABLE_STRIPE" == "yes" ]]; then
  prompt_secret STRIPE_SECRET_KEY "Stripe secret key"
  prompt_secret STRIPE_WEBHOOK_SECRET "Stripe webhook signing secret"
fi

RECAPTCHA_VERSION="v2"
RECAPTCHA_SITE_KEY=""
RECAPTCHA_SECRET_KEY=""
prompt_yes_no ENABLE_RECAPTCHA "Configure Google reCAPTCHA?" "n"
if [[ "$ENABLE_RECAPTCHA" == "yes" ]]; then
  prompt RECAPTCHA_VERSION "reCAPTCHA version (v2 or v3)" "v2"
  prompt_required RECAPTCHA_SITE_KEY "reCAPTCHA site key"
  prompt_secret RECAPTCHA_SECRET_KEY "reCAPTCHA secret key"
fi

{
  printf '# Taskoro configuration generated by setup.sh\n'
  printf '# Keep this file private. It contains application secrets.\n\n'
  write_env NODE_ENV "$NODE_ENV"
  write_env HOST "$HOST"
  write_env PORT "$PORT"
  write_env APP_BASE_URL "$APP_BASE_URL"
  write_env FRONTEND_URL "$FRONTEND_URL"
  write_env JWT_SECRET "$JWT_SECRET"
  write_env SESSION_SECRET "$SESSION_SECRET"
  if [[ -n "$DATABASE_URL" ]]; then
    write_env TASKORO_DATABASE_URL "$DATABASE_URL"
  else
    write_env DB_HOST "$DB_HOST"
    write_env DB_USER "$DB_USER"
    write_env DB_PASSWORD "$DB_PASSWORD"
    write_env DB_NAME "$DB_NAME"
    write_env DB_PORT "$DB_PORT"
  fi
  printf '\n# SMTP\n'
  write_env SMTP_HOST "$SMTP_HOST"
  write_env SMTP_PORT "$SMTP_PORT"
  write_env SMTP_ENCRYPTION "$SMTP_ENCRYPTION"
  write_env SMTP_SECURE "$SMTP_SECURE"
  write_env SMTP_FROM_ADDRESS "$SMTP_FROM_ADDRESS"
  write_env SMTP_FROM_NAME "$SMTP_FROM_NAME"
  write_env SMTP_USER "$SMTP_USER"
  write_env SMTP_PASS "$SMTP_PASS"
  printf '\n# Optional OAuth integrations\n'
  write_env SLACK_CLIENT_ID "$SLACK_CLIENT_ID"
  write_env SLACK_CLIENT_SECRET "$SLACK_CLIENT_SECRET"
  write_env ZOOM_CLIENT_ID "$ZOOM_CLIENT_ID"
  write_env ZOOM_CLIENT_SECRET "$ZOOM_CLIENT_SECRET"
  write_env GOOGLE_CLIENT_ID "$GOOGLE_CLIENT_ID"
  write_env GOOGLE_CLIENT_SECRET "$GOOGLE_CLIENT_SECRET"
  printf '\n# Optional payments and bot protection\n'
  write_env STRIPE_SECRET_KEY "$STRIPE_SECRET_KEY"
  write_env STRIPE_WEBHOOK_SECRET "$STRIPE_WEBHOOK_SECRET"
  write_env RECAPTCHA_VERSION "$RECAPTCHA_VERSION"
  write_env RECAPTCHA_SITE_KEY "$RECAPTCHA_SITE_KEY"
  write_env RECAPTCHA_SECRET_KEY "$RECAPTCHA_SECRET_KEY"
} >> "$TEMP_ENV"

mv "$TEMP_ENV" "$ENV_FILE"
KEEP_TEMP_ENV=0
chmod 600 "$ENV_FILE"
green "Configuration saved to backend/.env"

printf '\n'
blue "Installing dependencies"
npm ci --prefix "$ROOT_DIR/backend"
npm ci --prefix "$ROOT_DIR/frontend"

printf '\n'
blue "Building frontend and backend"
npm --prefix "$ROOT_DIR/frontend" run build
npm --prefix "$ROOT_DIR/backend" run build
green "Build completed successfully."

SYSTEMD_CREATED="no"
if command -v systemctl >/dev/null 2>&1 && [[ -d /run/systemd/system || "$(id -u)" -eq 0 ]]; then
  printf '\n'
  prompt_yes_no INSTALL_SERVICE "Create and start a systemd service for Taskoro?" "y"
  if [[ "$INSTALL_SERVICE" == "yes" ]]; then
    SERVICE_NAME="taskoro"
    SERVICE_USER="$(id -un)"
    NODE_BIN="$(command -v node)"
    SERVICE_FILE="/etc/systemd/system/${SERVICE_NAME}.service"
    if [[ "$(id -u)" -ne 0 ]]; then
      yellow "systemd service installation needs root privileges; skipping service creation."
    else
      cat > "$SERVICE_FILE" <<SERVICE
[Unit]
Description=Taskoro project management application
After=network.target

[Service]
Type=simple
User=${SERVICE_USER}
WorkingDirectory=${ROOT_DIR}/backend
EnvironmentFile=${ENV_FILE}
ExecStart=${NODE_BIN} ${ROOT_DIR}/backend/dist/server.js
Restart=always
RestartSec=5
NoNewPrivileges=true

[Install]
WantedBy=multi-user.target
SERVICE
      systemctl daemon-reload
      systemctl enable --now "$SERVICE_NAME"
      SYSTEMD_CREATED="yes"
      green "Taskoro systemd service is running."
    fi
  fi
fi

printf '\n'
blue "=========================================="
green "Taskoro installation completed"
blue "=========================================="
printf '\n'
printf 'Application URL: %s\n' "$FRONTEND_URL"
printf 'Health check:    %s/api/health\n' "$APP_BASE_URL"
printf '\n'
if [[ "$SYSTEMD_CREATED" == "yes" ]]; then
  printf '%s\n' "Service commands:"
  printf '  systemctl status taskoro\n'
  printf '  journalctl -u taskoro -f\n'
  printf '  systemctl restart taskoro\n'
else
  printf '%s\n' "Start the production server with:"
  printf '  NODE_ENV=production npm --prefix backend start\n'
fi
printf '\n'
yellow "Next steps:"
printf '%s\n' "1. Point your reverse proxy/firewall to the configured application port."
printf '%s\n' "2. For OAuth providers, register these callback URLs:"
printf '   %s/api/integrations/slack/callback\n' "$APP_BASE_URL"
printf '   %s/api/integrations/zoom/callback\n' "$APP_BASE_URL"
printf '   %s/api/integrations/google_calendar/callback\n' "$APP_BASE_URL"
printf '%s\n' "3. Sign in with the seeded admin account shown in the application login screen and change its password."