#!/bin/bash
# =============================================================================
# validate-env.sh — Validate .env for Changemaker Lite
#
# Checks required variables, secret strength, placeholder detection,
# production-mode requirements, and port conflicts.
#
# Usage:  ./scripts/validate-env.sh [--strict]
#   --strict: treat warnings as errors (for CI/pre-deploy)
#
# Exit codes:
#   0 = all checks passed
#   1 = errors found
#   2 = warnings found (only with --strict)
# =============================================================================

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_DIR="$(dirname "$SCRIPT_DIR")"
ENV_FILE="${PROJECT_DIR}/.env"

# Colors
RED='\033[0;31m'
YELLOW='\033[1;33m'
GREEN='\033[0;32m'
CYAN='\033[0;36m'
NC='\033[0m'

ERRORS=0
WARNINGS=0
STRICT=false

[[ "${1:-}" == "--strict" ]] && STRICT=true

# --- Helpers ---

error() {
  echo -e "  ${RED}ERROR${NC}  $1"
  ERRORS=$((ERRORS + 1))
}

warn() {
  echo -e "  ${YELLOW}WARN${NC}   $1"
  WARNINGS=$((WARNINGS + 1))
}

ok() {
  echo -e "  ${GREEN}OK${NC}     $1"
}

info() {
  echo -e "  ${CYAN}INFO${NC}   $1"
}

# --- Load .env ---

if [[ ! -f "$ENV_FILE" ]]; then
  echo -e "${RED}ERROR: .env file not found at ${ENV_FILE}${NC}"
  echo "  Copy .env.example to .env and configure it:"
  echo "  cp .env.example .env"
  exit 1
fi

# Source .env safely (export all, ignore errors from complex values)
set -a
# shellcheck disable=SC1090
source "$ENV_FILE" 2>/dev/null || true
set +a

echo ""
echo "========================================="
echo " Changemaker Lite — Environment Validator"
echo "========================================="
echo ""

# --- 1. Required Variables ---

echo "1. Required Variables"
echo "---------------------"

REQUIRED_VARS=(
  "V2_POSTGRES_PASSWORD"
  "REDIS_PASSWORD"
  "JWT_ACCESS_SECRET"
  "JWT_REFRESH_SECRET"
  "JWT_INVITE_SECRET"
  "DOMAIN"
  "INITIAL_ADMIN_EMAIL"
  "INITIAL_ADMIN_PASSWORD"
)

for var in "${REQUIRED_VARS[@]}"; do
  val="${!var:-}"
  if [[ -z "$val" ]]; then
    error "$var is not set"
  else
    ok "$var is set"
  fi
done

echo ""

# --- 2. Secret Strength ---

echo "2. Secret Strength"
echo "------------------"

check_secret_length() {
  local name="$1"
  local min_len="$2"
  local val="${!name:-}"
  if [[ -n "$val" ]] && [[ ${#val} -lt $min_len ]]; then
    error "$name is too short (${#val} chars, need $min_len+)"
  elif [[ -n "$val" ]]; then
    ok "$name length OK (${#val} chars)"
  fi
}

check_secret_length "JWT_ACCESS_SECRET" 32
check_secret_length "JWT_REFRESH_SECRET" 32
check_secret_length "JWT_INVITE_SECRET" 32
check_secret_length "V2_POSTGRES_PASSWORD" 8
check_secret_length "REDIS_PASSWORD" 8

# Password policy check (12+ chars, uppercase, lowercase, digit)
ADMIN_PW="${INITIAL_ADMIN_PASSWORD:-}"
if [[ -n "$ADMIN_PW" ]]; then
  PW_OK=true
  if [[ ${#ADMIN_PW} -lt 12 ]]; then
    error "INITIAL_ADMIN_PASSWORD too short (${#ADMIN_PW} chars, need 12+)"
    PW_OK=false
  fi
  if ! [[ "$ADMIN_PW" =~ [A-Z] ]]; then
    error "INITIAL_ADMIN_PASSWORD needs at least one uppercase letter"
    PW_OK=false
  fi
  if ! [[ "$ADMIN_PW" =~ [a-z] ]]; then
    error "INITIAL_ADMIN_PASSWORD needs at least one lowercase letter"
    PW_OK=false
  fi
  if ! [[ "$ADMIN_PW" =~ [0-9] ]]; then
    error "INITIAL_ADMIN_PASSWORD needs at least one digit"
    PW_OK=false
  fi
  if $PW_OK; then
    ok "INITIAL_ADMIN_PASSWORD meets password policy"
  fi
fi

echo ""

# --- 3. Placeholder Detection ---

echo "3. Placeholder Detection"
echo "------------------------"

PLACEHOLDER_PATTERNS=("CHANGE_THIS" "REQUIRED" "changeme" "password123" "secret123" "example.com" "your-")

for var in JWT_ACCESS_SECRET JWT_REFRESH_SECRET JWT_INVITE_SECRET V2_POSTGRES_PASSWORD REDIS_PASSWORD ENCRYPTION_KEY; do
  val="${!var:-}"
  if [[ -z "$val" ]]; then continue; fi
  for pattern in "${PLACEHOLDER_PATTERNS[@]}"; do
    if echo "$val" | grep -qi "$pattern"; then
      error "$var contains placeholder value '$pattern'"
    fi
  done
done

# Check secrets are not reused
if [[ -n "${JWT_ACCESS_SECRET:-}" ]] && [[ "${JWT_ACCESS_SECRET:-}" == "${JWT_REFRESH_SECRET:-}" ]]; then
  error "JWT_ACCESS_SECRET and JWT_REFRESH_SECRET must be different"
fi

if [[ -n "${ENCRYPTION_KEY:-}" ]] && [[ "${ENCRYPTION_KEY:-}" == "${JWT_ACCESS_SECRET:-}" ]]; then
  error "ENCRYPTION_KEY must not reuse JWT_ACCESS_SECRET"
fi

ok "Placeholder check complete"
echo ""

# --- 4. Production Checks ---

echo "4. Production Checks"
echo "--------------------"

NODE_ENV="${NODE_ENV:-development}"
info "NODE_ENV=$NODE_ENV"

if [[ "$NODE_ENV" == "production" ]]; then
  if [[ -z "${ENCRYPTION_KEY:-}" ]]; then
    error "ENCRYPTION_KEY is required in production"
  else
    ok "ENCRYPTION_KEY is set"
  fi

  if [[ "${EMAIL_TEST_MODE:-}" == "true" ]]; then
    warn "EMAIL_TEST_MODE=true in production (emails go to MailHog, not SMTP)"
  fi

  CORS="${CORS_ORIGINS:-}"
  if echo "$CORS" | grep -q "localhost"; then
    warn "CORS_ORIGINS contains 'localhost' in production"
  fi

  if [[ -z "${CORS:-}" ]]; then
    warn "CORS_ORIGINS is not set — API will reject cross-origin requests"
  else
    ok "CORS_ORIGINS configured"
  fi
else
  info "Skipping production-only checks (NODE_ENV=$NODE_ENV)"
fi

echo ""

# --- 5. Port Conflict Detection ---

echo "5. Port Conflict Detection"
echo "--------------------------"

# Collect all configured ports
declare -A PORT_MAP
PORT_VARS=(
  "ADMIN_PORT:3000"
  "API_PORT:4000"
  "MEDIA_API_PORT:4100"
  "V2_POSTGRES_PORT:5433"
  "GRAFANA_PORT:3001"
  "HOMEPAGE_PORT:3010"
  "GITEA_PORT:3030"
  "MKDOCS_DEV_PORT:4003"
  "NOCODB_PORT:8091"
  "MAILHOG_PORT:8025"
  "LISTMONK_PORT:9001"
  "N8N_PORT:5678"
  "CODE_SERVER_PORT:8888"
  "PROMETHEUS_PORT:9090"
)

DUPLICATE_FOUND=false
for entry in "${PORT_VARS[@]}"; do
  var="${entry%%:*}"
  default="${entry##*:}"
  port="${!var:-$default}"

  if [[ -n "${PORT_MAP[$port]:-}" ]]; then
    error "Port $port conflict: ${PORT_MAP[$port]} and $var"
    DUPLICATE_FOUND=true
  else
    PORT_MAP[$port]="$var"
  fi
done

if ! $DUPLICATE_FOUND; then
  ok "No port conflicts detected"
fi

echo ""

# --- 5b. Host Port Availability ---
# Detects processes already bound to ports we intend to use (e.g. cockpit on :9090).

echo "5b. Host Port Availability"
echo "--------------------------"

if ! command -v ss >/dev/null 2>&1; then
  warn "ss not installed — skipping host port check (install iproute2 to enable)"
else
  # Collect unique ports from PORT_MAP keys
  HOST_CONFLICTS=()
  for port in "${!PORT_MAP[@]}"; do
    # ss -H (no header) -t (tcp) -l (listen) -n (numeric); match :PORT at end of local addr
    # Also matches *:PORT and [::]:PORT
    if ss -Htln 2>/dev/null | awk -v p=":$port" '$4 ~ p"$" {found=1} END{exit !found}'; then
      owner="${PORT_MAP[$port]}"
      # Process identification needs root; ss -tlnp includes users:(("name",pid=X,fd=Y))
      proc=""
      if [[ $EUID -eq 0 ]]; then
        proc=$(ss -Htlnp 2>/dev/null | awk -v p=":$port" '$4 ~ p"$" {print; exit}' \
               | grep -oP 'users:\(\("[^"]+"' | head -1 | sed 's/users:(("//')
      fi
      if [[ -n "$proc" ]]; then
        error "Port $port already in use by '$proc' (want: $owner)"
      else
        error "Port $port already in use on host (want: $owner) — run as root to identify the process"
      fi
      HOST_CONFLICTS+=("$port")
    fi
  done

  if [[ ${#HOST_CONFLICTS[@]} -eq 0 ]]; then
    ok "All configured host ports are available"
  else
    info "Common culprits: cockpit.socket (9090), systemd-resolved (53), apache2/nginx host (80/443)"
    info "Fix: \`sudo systemctl stop <service> && sudo systemctl disable <service>\` or remap the port in .env"
  fi
fi

echo ""

# --- 6. Feature Flag Consistency ---

echo "6. Feature Flag Consistency"
echo "---------------------------"

if [[ "${ENABLE_MEDIA_FEATURES:-}" == "true" ]]; then
  ok "Media features enabled"
fi

if [[ "${LISTMONK_SYNC_ENABLED:-}" == "true" ]]; then
  if [[ -z "${LISTMONK_ADMIN_USER:-}" ]] || [[ -z "${LISTMONK_ADMIN_PASSWORD:-}" ]]; then
    warn "LISTMONK_SYNC_ENABLED=true but LISTMONK_ADMIN_USER/PASSWORD not set"
  else
    ok "Listmonk sync credentials configured"
  fi
fi

if [[ "${ENABLE_PAYMENTS:-}" == "true" ]]; then
  if [[ -z "${STRIPE_SECRET_KEY:-}" ]]; then
    warn "ENABLE_PAYMENTS=true but STRIPE_SECRET_KEY not set"
  fi
fi

echo ""

# --- Summary ---

echo "========================================="
if [[ $ERRORS -gt 0 ]]; then
  echo -e " ${RED}FAILED${NC}: $ERRORS error(s), $WARNINGS warning(s)"
  echo "========================================="
  exit 1
elif [[ $WARNINGS -gt 0 ]] && $STRICT; then
  echo -e " ${YELLOW}WARNINGS${NC}: $WARNINGS warning(s) (strict mode)"
  echo "========================================="
  exit 2
elif [[ $WARNINGS -gt 0 ]]; then
  echo -e " ${YELLOW}PASSED${NC} with $WARNINGS warning(s)"
  echo "========================================="
  exit 0
else
  echo -e " ${GREEN}PASSED${NC}: All checks OK"
  echo "========================================="
  exit 0
fi
