#!/usr/bin/env bash
set -euo pipefail

# Colors
RESET="\033[0m"
GREEN="\033[32m"
CYAN="\033[36m"
YELLOW="\033[33m"
RED="\033[31m"

REPO_ROOT="$(cd "$(dirname "$0")"/.. && pwd)"
BACKEND_DIR="$REPO_ROOT/backend"
FRONTEND_DIR="$REPO_ROOT/frontend"
CONFIG_FILE="$REPO_ROOT/config/config.json"

# Guard to ensure cleanup runs only once
__CLEANED_UP=0

# Port configuration
BACKEND_PORT=8000
FRONTEND_PORT=3001

# Kill process using specified port
kill_port() {
  local port=$1
  echo -e "${YELLOW}[DEBUG]${RESET} Checking port $port..."
  local pids=$(lsof -nP -ti tcp:"$port" 2>/dev/null || true)
  echo -e "${YELLOW}[DEBUG]${RESET} Done checking port $port. PIDs: '$pids'"
  if [[ -n "$pids" ]]; then
    echo -e "${YELLOW}[CLEANUP]${RESET} Killing process(es) using port $port: $pids"
    echo "$pids" | xargs kill -9 2>/dev/null || true
    sleep 1
  fi
}

ensure_backend() {
  cd "$BACKEND_DIR"
  if [[ ! -d .venv ]]; then
    echo -e "${YELLOW}[SETUP]${RESET} Creating Python venv and installing backend requirements..."
    python3 -m venv .venv
    source .venv/bin/activate
    # Upgrade pip first
    pip install --upgrade pip --timeout 100 -i https://pypi.tuna.tsinghua.edu.cn/simple || \
      pip install --upgrade pip --timeout 100
    # Install requirements with timeout and mirror
    pip install -r requirements.txt --timeout 300 -i https://pypi.tuna.tsinghua.edu.cn/simple || \
      pip install -r requirements.txt --timeout 300
  else
    source .venv/bin/activate
    # Upgrade pip if needed
    pip install --upgrade pip --timeout 100 -i https://pypi.tuna.tsinghua.edu.cn/simple 2>/dev/null || \
      pip install --upgrade pip --timeout 100 2>/dev/null || true
    # Check if key dependencies are installed
    if ! python -c "import uvicorn" 2>/dev/null; then
      echo -e "${YELLOW}[SETUP]${RESET} Installing missing backend requirements..."
      pip install -r requirements.txt --timeout 300 -i https://pypi.tuna.tsinghua.edu.cn/simple || \
        pip install -r requirements.txt --timeout 300
    fi
  fi

  # Export TUSHARE_TOKEN from config if available (prefer jq, fallback to node)
  if [[ -f "$CONFIG_FILE" ]]; then
    if command -v jq >/dev/null 2>&1; then
      TUSHARE_TOKEN_VAL=$(jq -r '.data_sources.tushare.api_key // empty' "$CONFIG_FILE" 2>/dev/null || true)
    else
      TUSHARE_TOKEN_VAL=$(node -e "const fs=require('fs');try{const c=JSON.parse(fs.readFileSync(process.argv[1],'utf8'));const v=c?.data_sources?.tushare?.api_key||'';if(v)process.stdout.write(v)}catch{}" "$CONFIG_FILE" 2>/dev/null || true)
    fi
    if [[ -n "${TUSHARE_TOKEN_VAL:-}" ]]; then
      export TUSHARE_TOKEN="$TUSHARE_TOKEN_VAL"
    fi
  fi
}

run_backend() {
  ensure_backend
  cd "$BACKEND_DIR"
  # Run and colorize output (avoid stdbuf on macOS)
  UVICORN_CMD=(uvicorn app.main:app --reload --port "$BACKEND_PORT" --log-level info)
  "${UVICORN_CMD[@]}" 2>&1 | while IFS= read -r line; do
    printf "%b[%s] [BACKEND] %s%b\n" "$GREEN" "$(date '+%Y-%m-%d %H:%M:%S')" "$line" "$RESET"
  done
}

ensure_frontend() {
  cd "$FRONTEND_DIR"
  if [[ ! -d node_modules ]]; then
    echo -e "${YELLOW}[SETUP]${RESET} Installing frontend dependencies (npm install)..."
    npm install --no-fund --no-audit
  fi
}

run_frontend() {
  ensure_frontend
  cd "$FRONTEND_DIR"
  npm run dev 2>&1 | while IFS= read -r line; do
    printf "%b[%s] [FRONTEND] %s%b\n" "$CYAN" "$(date '+%Y-%m-%d %H:%M:%S')" "$line" "$RESET"
  done
}

# Recursively kill a process tree (children first), with optional signal (default TERM)
kill_tree() {
  local pid="$1"
  local signal="${2:-TERM}"
  if [[ -z "${pid:-}" ]]; then
    return
  fi
  # Kill children first
  local children
  children=$(pgrep -P "$pid" 2>/dev/null || true)
  if [[ -n "${children:-}" ]]; then
    for child in $children; do
      kill_tree "$child" "$signal"
    done
  fi
  # Then the parent
  kill -"$signal" "$pid" 2>/dev/null || true
}

cleanup() {
  # Ensure this runs only once even if multiple signals (INT/TERM/EXIT) arrive
  if [[ $__CLEANED_UP -eq 1 ]]; then
    return
  fi
  __CLEANED_UP=1

  echo -e "\n${YELLOW}[CLEANUP]${RESET} Stopping services..."

  # Gracefully stop trees for backend and frontend, then escalate if needed
  if [[ -n "${BACKEND_PID:-}" ]]; then
    kill_tree "$BACKEND_PID" TERM
  fi
  if [[ -n "${FRONTEND_PID:-}" ]]; then
    kill_tree "$FRONTEND_PID" TERM
  fi

  # Wait up to ~3s for graceful shutdown
  for _ in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15; do
    local backend_alive=0 frontend_alive=0
    if [[ -n "${BACKEND_PID:-}" ]] && kill -0 "$BACKEND_PID" 2>/dev/null; then backend_alive=1; fi
    if [[ -n "${FRONTEND_PID:-}" ]] && kill -0 "$FRONTEND_PID" 2>/dev/null; then frontend_alive=1; fi
    if [[ $backend_alive -eq 0 && $frontend_alive -eq 0 ]]; then
      break
    fi
    sleep 0.2
  done

  # Escalate to KILL if still alive
  if [[ -n "${BACKEND_PID:-}" ]] && kill -0 "$BACKEND_PID" 2>/dev/null; then
    kill_tree "$BACKEND_PID" KILL
  fi
  if [[ -n "${FRONTEND_PID:-}" ]] && kill -0 "$FRONTEND_PID" 2>/dev/null; then
    kill_tree "$FRONTEND_PID" KILL
  fi

  # As a final safeguard, free the ports
  kill_port "$BACKEND_PORT"
  kill_port "$FRONTEND_PORT"

  echo -e "${GREEN}[CLEANUP]${RESET} All services stopped."
}

main() {
  echo -e "${CYAN}Dev Launcher${RESET}: Starting Backend ($BACKEND_PORT) and Frontend ($FRONTEND_PORT)\n"
  
  # Clean up ports before starting
  kill_port "$BACKEND_PORT"
  kill_port "$FRONTEND_PORT"
  
  echo -e "${GREEN}[$(date '+%Y-%m-%d %H:%M:%S')] [BACKEND]${RESET} API:   http://127.0.0.1:$BACKEND_PORT"
  echo -e "${CYAN}[$(date '+%Y-%m-%d %H:%M:%S')] [FRONTEND]${RESET} APP:  http://127.0.0.1:$FRONTEND_PORT\n"

  run_backend & BACKEND_PID=$!
  run_frontend & FRONTEND_PID=$!

  trap cleanup INT TERM EXIT

  # Wait on both; if either exits, we exit (portable)
  while true; do
    if ! kill -0 "$BACKEND_PID" 2>/dev/null; then break; fi
    if ! kill -0 "$FRONTEND_PID" 2>/dev/null; then break; fi
    sleep 1
  done
}

main "$@"
