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

ROOT_DIR="$(cd "$(dirname "$0")" && pwd)"
RUN_DIR="$ROOT_DIR/run"
LOG_DIR="$ROOT_DIR/logs"
API_PID_FILE="$RUN_DIR/api.pid"
WORKER_PID_FILE="$RUN_DIR/worker.pid"
API_LOG_FILE="$LOG_DIR/api.log"
WORKER_LOG_FILE="$LOG_DIR/worker.log"
API_BIN="$RUN_DIR/api"
WORKER_BIN="$RUN_DIR/worker"
HTTP_PORT=16811
WORKER_HTTP_PORT=16812

mkdir -p "$RUN_DIR" "$LOG_DIR"

load_env_config() {
  local env_file

  for env_file in ".env"; do
    if [[ -f "$ROOT_DIR/$env_file" ]]; then
      # shellcheck disable=SC1090
      source "$ROOT_DIR/$env_file"
    fi
  done

  if [[ -n "${APP_ENV:-}" && -f "$ROOT_DIR/.env.$APP_ENV" ]]; then
    # shellcheck disable=SC1090
    source "$ROOT_DIR/.env.$APP_ENV"
  elif [[ -n "${GO_ENV:-}" && -f "$ROOT_DIR/.env.$GO_ENV" ]]; then
    # shellcheck disable=SC1090
    source "$ROOT_DIR/.env.$GO_ENV"
  elif [[ -n "${ENV:-}" && -f "$ROOT_DIR/.env.$ENV" ]]; then
    # shellcheck disable=SC1090
    source "$ROOT_DIR/.env.$ENV"
  fi

  if [[ -f "$ROOT_DIR/.env.local" ]]; then
    # shellcheck disable=SC1090
    source "$ROOT_DIR/.env.local"
  fi
}

port_pid() {
  local port="$1"
  lsof -tiTCP:"$port" -sTCP:LISTEN 2>/dev/null | head -n 1 || true
}

is_scheduler_process() {
  local pid="$1"
  local cmd

  cmd="$(ps -p "$pid" -o command= 2>/dev/null || true)"
  [[ "$cmd" == *"/scheduler-backend/run/api"* || "$cmd" == *"/scheduler-backend/run/worker"* || "$cmd" == *"go run ./cmd/api"* || "$cmd" == *"go run ./cmd/worker"* || "$cmd" == *"/scheduler-backend/api"* || "$cmd" == *"/scheduler-backend/worker"* || "$cmd" == *"/exe/api"* || "$cmd" == *"/exe/worker"* ]]
}

is_running() {
  local pid_file="$1"
  if [[ ! -f "$pid_file" ]]; then
    return 1
  fi

  local pid
  pid="$(cat "$pid_file")"
  if [[ -z "$pid" ]]; then
    return 1
  fi

  kill -0 "$pid" 2>/dev/null
}

ensure_port_free() {
  local name="$1"
  local port="$2"
  local pid

  pid="$(port_pid "$port")"
  if [[ -z "$pid" ]]; then
    return 0
  fi

  if is_scheduler_process "$pid"; then
    kill "$pid" 2>/dev/null || true
    sleep 1
    pid="$(port_pid "$port")"
    if [[ -z "$pid" ]]; then
      echo "$name previous process stopped, port=$port"
      return 0
    fi
  fi

  echo "$name port $port is already occupied by pid=$pid" >&2
  ps -p "$pid" -o pid,ppid,command >&2 || true
  return 1
}

start_process() {
  local name="$1"
  local pid_file="$2"
  local log_file="$3"
  shift 3

  if is_running "$pid_file"; then
    echo "$name is already running, restarting pid=$(cat "$pid_file")"
    kill "$(cat "$pid_file")" 2>/dev/null || true
    sleep 1
  fi

  rm -f "$pid_file"
  (
    cd "$ROOT_DIR"
    nohup "$@" >>"$log_file" 2>&1 &
    echo $! >"$pid_file"
  )

  sleep 1
  if is_running "$pid_file"; then
    echo "$name started, pid=$(cat "$pid_file"), log=$log_file"
    return 0
  fi

  echo "failed to start $name, check $log_file" >&2
  rm -f "$pid_file"
  return 1
}

build_binary() {
  local output="$1"
  local target="$2"

  (
    cd "$ROOT_DIR"
    GOWORK=off go build -o "$output" "$target"
  )
}

stop_process() {
  local name="$1"
  local pid_file="$2"

  if ! is_running "$pid_file"; then
    rm -f "$pid_file"
    return 0
  fi

  kill "$(cat "$pid_file")" 2>/dev/null || true
  rm -f "$pid_file"
  echo "$name stopped"
}

build_binary "$API_BIN" ./cmd/api
build_binary "$WORKER_BIN" ./cmd/worker

load_env_config
ensure_port_free "api" "${HTTP_PORT}"
ensure_port_free "worker" "${WORKER_HTTP_PORT}"

start_process "api" "$API_PID_FILE" "$API_LOG_FILE" "$API_BIN"
if ! start_process "worker" "$WORKER_PID_FILE" "$WORKER_LOG_FILE" "$WORKER_BIN"; then
  stop_process "api" "$API_PID_FILE"
  exit 1
fi

echo "scheduler-backend started"
echo "api pid: $(cat "$API_PID_FILE")"
echo "worker pid: $(cat "$WORKER_PID_FILE")"
echo "api log: $API_LOG_FILE"
echo "worker log: $WORKER_LOG_FILE"
