#!/usr/bin/env bash
# scripts/startup.sh
# Startup helper for tdc114plus development environment
# Usage: ./scripts/startup.sh [--dry-run] [--auto] [--wait=N] [--skip-android-check]

set -euo pipefail

ROOT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
BARON_DIR="${BARON_SSO_WORKTREE:-/home/ubuntu/workspace/baron-sso-tdc114plus-api}"
LOG_BASE_DIR="${TDC114_LOG_BASE:-$ROOT_DIR/logs}"
LOG_DIR="$LOG_BASE_DIR/$(date +%F)"
LOG_FILE="$LOG_DIR/startup.log"
CHECK_SCRIPT="${TDC114_CHECK_SCRIPT:-$ROOT_DIR/scripts/check-baron-api-env.sh}"
API_SMOKE_SCRIPT="${TDC114_API_SMOKE_SCRIPT:-$ROOT_DIR/scripts/api-smoke.sh}"
DOCKER_BIN="${DOCKER_BIN:-docker}"
MAKE_BIN="${MAKE_BIN:-make}"
CHMOD_BIN="${CHMOD_BIN:-chmod}"
REQUIRE_AUTH_SMOKE="${TDC114_REQUIRE_AUTH_SMOKE:-false}"
ANDROID_PRECHECK_SCRIPT="${TDC114_ANDROID_PRECHECK_SCRIPT:-$ROOT_DIR/scripts/check-android-device-env.sh}"
AUTH_SERVER_SCRIPT="${TDC114_AUTH_SERVER_SCRIPT:-$ROOT_DIR/scripts/start-auth-server.sh}"
START_AUTH_SERVER="${TDC114_START_AUTH_SERVER:-true}"
DAILY_HANDOFF_DIR="${TDC114_DAILY_HANDOFF_DIR:-$ROOT_DIR/docs/daily-issues}"
REQUIRE_DAILY_HANDOFF="${TDC114_REQUIRE_DAILY_HANDOFF:-true}"
WSL_MAINTENANCE_SCRIPT="${TDC114_WSL_MAINTENANCE_SCRIPT:-$ROOT_DIR/scripts/check-wsl-maintenance.sh}"
WSL_MAINTENANCE_ENABLED="${TDC114_WSL_MAINTENANCE_ENABLED:-true}"

DRY_RUN=true
AUTO=false
WAIT=30
RETRIES=6
SLEEP_BETWEEN=10
SKIP_ANDROID_CHECK=false

for arg in "$@"; do
  case "$arg" in
    --dry-run) DRY_RUN=true ;;
    --auto) DRY_RUN=false; AUTO=true ;;
    --wait=*) WAIT=${arg#*=} ;;
    --skip-android-check|--skip-device-check) SKIP_ANDROID_CHECK=true ;;
    --help|-h)
      cat <<EOF
Usage: $0 [--dry-run] [--auto] [--wait=N] [--skip-android-check]

--dry-run  : show commands and write non-destructive logs (default)
--auto     : actually perform startup actions
--wait=N   : seconds to wait for services to stabilize (default 30)
--skip-android-check : skip Android target preflight (not recommended)
--help     : show this message
EOF
      exit 0
      ;;
    --wait) echo "Please use --wait=N" >&2; exit 2 ;;
    *) echo "Unknown arg: $arg" >&2; exit 2 ;;
  esac
done

case "$WAIT" in
  ''|*[!0-9]*)
    echo "--wait must be a non-negative integer" >&2
    exit 2
    ;;
esac

mkdir -p "$LOG_DIR"

log() {
  echo "$*" | tee -a "$LOG_FILE"
}

run_cmd() {
  log "+ $*"
  if [ "$DRY_RUN" = false ]; then
    set +e
    bash -lc "$*" 2>&1 | tee -a "$LOG_FILE"
    local rc=${PIPESTATUS[0]}
    set -e
    return "$rc"
  fi
}

run_cmd_capture() {
  local output_file="$1"
  shift

  log "+ $*"
  if [ "$DRY_RUN" = false ]; then
    set +e
    bash -lc "$*" 2>&1 | tee "$output_file" | tee -a "$LOG_FILE"
    local rc=${PIPESTATUS[0]}
    set -e
    return "$rc"
  fi
}

compose_cmd() {
  printf "%s compose -f docker-compose.yaml -f compose.infra.yaml -f compose.ory.yaml %s" \
    "$DOCKER_BIN" "$1"
}

ensure_required_paths() {
  if [ ! -d "$BARON_DIR" ]; then
    log "Startup aborted: Baron worktree not found at $BARON_DIR"
    exit 1
  fi
  if [ ! -x "$CHECK_SCRIPT" ]; then
    log "Startup aborted: check script is missing or not executable at $CHECK_SCRIPT"
    exit 1
  fi
  if [ ! -x "$API_SMOKE_SCRIPT" ]; then
    log "Startup aborted: api smoke script is missing or not executable at $API_SMOKE_SCRIPT"
    exit 1
  fi
  if [ "$SKIP_ANDROID_CHECK" = false ] && [ ! -x "$ANDROID_PRECHECK_SCRIPT" ]; then
    log "Startup aborted: Android precheck script is missing or not executable at $ANDROID_PRECHECK_SCRIPT"
    exit 1
  fi
  if [ "$START_AUTH_SERVER" = true ] && [ ! -x "$AUTH_SERVER_SCRIPT" ]; then
    log "Startup aborted: auth server script is missing or not executable at $AUTH_SERVER_SCRIPT"
    exit 1
  fi
  if [ ! -d "$DAILY_HANDOFF_DIR" ]; then
    log "Startup aborted: daily handoff directory is missing at $DAILY_HANDOFF_DIR"
    exit 1
  fi
  if [ "$WSL_MAINTENANCE_ENABLED" = true ] && [ ! -x "$WSL_MAINTENANCE_SCRIPT" ]; then
    log "Startup aborted: WSL maintenance script is missing or not executable at $WSL_MAINTENANCE_SCRIPT"
    exit 1
  fi
}

review_daily_handoff() {
  local today
  local handoff_file

  today="$(date +%F)"
  handoff_file="$(find "$DAILY_HANDOFF_DIR" -maxdepth 1 -type f -name '20*.md' ! -name "$today*" | sort | tail -n 1 || true)"

  if [ -z "$handoff_file" ]; then
    log "Daily handoff review: no previous handoff file found in $DAILY_HANDOFF_DIR"
    if [ "$REQUIRE_DAILY_HANDOFF" = true ]; then
      log "Startup aborted: previous daily handoff is required. Set TDC114_REQUIRE_DAILY_HANDOFF=false only for exceptional recovery."
      exit 1
    fi
    return 0
  fi

  log "Daily handoff review required before startup"
  log "Daily handoff file: $handoff_file"
  log "----- daily handoff excerpt start -----"
  sed -n '1,160p' "$handoff_file" | tee -a "$LOG_FILE"
  log "----- daily handoff excerpt end -----"
}

review_wsl_maintenance() {
  if [ "$WSL_MAINTENANCE_ENABLED" != true ]; then
    log "WSL maintenance reminder skipped by TDC114_WSL_MAINTENANCE_ENABLED=$WSL_MAINTENANCE_ENABLED"
    return 0
  fi

  log "WSL maintenance review started: $WSL_MAINTENANCE_SCRIPT status"
  set +e
  (
    cd "$ROOT_DIR"
    "$WSL_MAINTENANCE_SCRIPT" status
  ) 2>&1 | tee -a "$LOG_FILE"
  local rc=${PIPESTATUS[0]}
  set -e

  if [ "$rc" -ne 0 ]; then
    log "Startup aborted: WSL maintenance review script failed"
    exit 1
  fi
}

run_android_precheck() {
  if [ "$SKIP_ANDROID_CHECK" = true ]; then
    log "Android target preflight skipped by option"
    return 0
  fi

  log "Android target preflight started: $ANDROID_PRECHECK_SCRIPT"
  set +e
  (
    cd "$ROOT_DIR"
    "$ANDROID_PRECHECK_SCRIPT"
  ) 2>&1 | tee -a "$LOG_FILE"
  local rc=${PIPESTATUS[0]}
  set -e

  if [ "$rc" -ne 0 ]; then
    log "Startup paused: Android target preflight did not pass"
    log "Resolve the physical-device or ADB steps above, then rerun startup."
    exit 1
  fi

  log "Android target preflight passed"
}

ensure_auth_server() {
  if [ "$START_AUTH_SERVER" != true ]; then
    log "tdc114plus-auth startup skipped by TDC114_START_AUTH_SERVER=$START_AUTH_SERVER"
    return 0
  fi

  log "tdc114plus-auth startup started: $AUTH_SERVER_SCRIPT --restart"
  set +e
  (
    cd "$ROOT_DIR"
    "$AUTH_SERVER_SCRIPT" --restart
  ) 2>&1 | tee -a "$LOG_FILE"
  local rc=${PIPESTATUS[0]}
  set -e

  if [ "$rc" -ne 0 ]; then
    log "Startup aborted: tdc114plus-auth did not start"
    exit 1
  fi

  log "tdc114plus-auth ready"
}

ensure_generated_config_writable() {
  local generated_dir="$BARON_DIR/config/.generated"

  run_cmd "mkdir -p '$generated_dir'"
  run_cmd "cd '$BARON_DIR' && $CHMOD_BIN -R u+w config/.generated 2>/dev/null || true"

  if [ "$DRY_RUN" = false ] && [ ! -w "$generated_dir" ]; then
    log "Startup aborted: $generated_dir is not writable by $(id -un). Fix ownership/permissions before startup."
    exit 1
  fi
}

sanitize_rendered_kratos_config() {
  local kratos_config="$BARON_DIR/config/.generated/ory/kratos/kratos.yml"

  if [ ! -f "$kratos_config" ]; then
    log "Startup aborted: rendered Kratos config is missing at $kratos_config"
    exit 1
  fi

  if grep -Fq "        - []" "$kratos_config"; then
    log "Rendered Kratos config contains invalid empty allowed_return_urls entry; removing it automatically"
    set +e
    perl -0pi -e 's/^[ ]*-\s*\[\]\n//mg' "$kratos_config"
    local rc=$?
    set -e
    if [ "$rc" -ne 0 ]; then
      log "Startup aborted: failed to sanitize $kratos_config"
      exit 1
    fi
  fi
}

ensure_rendered_ory_files() {
  local oathkeeper_entrypoint="$BARON_DIR/config/.generated/ory/oathkeeper/entrypoint.sh"

  sanitize_rendered_kratos_config

  if [ ! -x "$oathkeeper_entrypoint" ]; then
    log "Startup aborted: rendered Oathkeeper entrypoint is missing or not executable at $oathkeeper_entrypoint"
    exit 1
  fi
}

capture_failure_diagnostics() {
  local prefix="${1:-runtime}"

  if [ "$DRY_RUN" = true ]; then
    return 0
  fi

  run_cmd "mkdir -p '$LOG_DIR'"
  run_cmd "cd '$BARON_DIR' && $(compose_cmd "ps") > '$LOG_DIR/${prefix}-compose-ps.log' 2>&1 || true"
  run_cmd "$DOCKER_BIN ps -a --format '{{.Names}} {{.Status}}' | grep -E 'baron|ory' > '$LOG_DIR/${prefix}-docker-ps.log' 2>&1 || true"
  run_cmd "$DOCKER_BIN logs --tail 120 ory_kratos > '$LOG_DIR/${prefix}-ory_kratos.log' 2>&1 || true"
  run_cmd "$DOCKER_BIN logs --tail 120 ory_stack_check > '$LOG_DIR/${prefix}-ory_stack_check.log' 2>&1 || true"
  run_cmd "$DOCKER_BIN logs --tail 120 oathkeeper > '$LOG_DIR/${prefix}-oathkeeper.log' 2>&1 || true"
  run_cmd "$DOCKER_BIN logs --tail 120 baron_backend > '$LOG_DIR/${prefix}-baron_backend.log' 2>&1 || true"
}

run_check_script() {
  local attempt="$1"
  local output_file="$LOG_DIR/check-baron-api-env.attempt-${attempt}.log"

  set +e
  (
    cd "$ROOT_DIR"
    "$CHECK_SCRIPT"
  ) 2>&1 | tee "$output_file" | tee -a "$LOG_FILE"
  local rc=${PIPESTATUS[0]}
  set -e

  if [ "$rc" -ne 0 ]; then
    log "check-baron-api-env.sh failed on attempt $attempt"
    return 1
  fi

  if ! grep -Fq "RESULT ready-ish: 0 failure(s), 0 warning(s)" "$output_file"; then
    log "check-baron-api-env.sh reported warnings on attempt $attempt"
    return 1
  fi

  log "check-baron-api-env.sh passed with zero warnings"
  return 0
}

run_api_smoke_script() {
  local attempt="$1"
  local output_file="$LOG_DIR/api-smoke.attempt-${attempt}.log"
  local fallback_output_file="$LOG_DIR/api-smoke.base-only.attempt-${attempt}.log"

  set +e
  (
    cd "$ROOT_DIR"
    "$API_SMOKE_SCRIPT"
  ) 2>&1 | tee "$output_file" | tee -a "$LOG_FILE"
  local rc=${PIPESTATUS[0]}
  set -e

  if [ "$rc" -ne 0 ]; then
    if [ "$REQUIRE_AUTH_SMOKE" != "true" ] && grep -Fq '"code":"login_failed"' "$output_file"; then
      log "api-smoke authenticated login failed; retrying in base-smoke-only mode because TDC114_REQUIRE_AUTH_SMOKE is false"
      set +e
      (
        cd "$ROOT_DIR"
        TDC114_SKIP_AUTH_SMOKE=true "$API_SMOKE_SCRIPT"
      ) 2>&1 | tee "$fallback_output_file" | tee -a "$LOG_FILE"
      rc=${PIPESTATUS[0]}
      set -e

      if [ "$rc" -eq 0 ]; then
        log "api-smoke base-smoke-only mode passed"
        return 0
      fi
    fi

    log "api-smoke.sh failed on attempt $attempt"
    return 1
  fi

  log "api-smoke.sh passed"
  return 0
}

start_stack() {
  local down_cmd="cd '$BARON_DIR' && $(compose_cmd "down")"
  local up_cmd="cd '$BARON_DIR' && $(compose_cmd "up -d")"
  local output_file="$LOG_DIR/compose-up.initial.log"

  run_cmd_capture "$LOG_DIR/compose-down.initial.log" "$down_cmd" || true

  if run_cmd_capture "$output_file" "$up_cmd"; then
    return 0
  fi

  if grep -Eq "name conflict|is already in use by container" "$output_file"; then
    log "compose up failed due to stale or conflicting containers; attempting automatic cleanup"
    run_cmd "cd '$BARON_DIR' && $(compose_cmd "down") >/dev/null 2>&1 || true"
    run_cmd "$DOCKER_BIN ps -a --format '{{.Names}}' | grep -iE 'baron|ory' | xargs -r $DOCKER_BIN rm -f || true"
    run_cmd_capture "$LOG_DIR/compose-up.retry.log" "$up_cmd"
    return $?
  fi

  if grep -Eq "dependency failed to start|didn't complete successfully|is unhealthy" "$output_file"; then
    log "compose up reported dependency or health issues; continuing to stabilization checks before declaring failure"
    return 0
  fi

  return 1
}

ensure_required_paths

log "Startup started: $(date)"

# 0) Always review the previous daily handoff before starting runtime work.
review_daily_handoff
review_wsl_maintenance

# 1) Verify Android target readiness before backend startup work
run_android_precheck

# 2) Ensure networks and config rendered (if Makefile target exists)
if [ -f "$BARON_DIR/Makefile" ]; then
  ensure_generated_config_writable
  run_cmd "cd '$BARON_DIR' && $MAKE_BIN ensure-networks || true"
  if ! run_cmd "cd '$BARON_DIR' && $MAKE_BIN render-ory-config"; then
    log "Startup aborted: make render-ory-config failed"
    exit 1
  fi
  ensure_rendered_ory_files
fi

# 3) Start all necessary compose stacks, retrying once after conflict cleanup
start_stack

# 4) Wait for services to stabilize
run_cmd "sleep $WAIT"

# 5) Show container status
run_cmd "$DOCKER_BIN ps --format '{{.Names}} {{.Status}}' | grep -E 'baron|ory' || true"

# 5-1) Start local auth broker used by the app.
ensure_auth_server

# 6) Run health checks and smoke tests with retries
startup_ok=false
i=1
while [ "$i" -le "$RETRIES" ]; do
  log "Health check attempt $i/$RETRIES"
  if [ "$DRY_RUN" = false ]; then
    if run_check_script "$i" && run_api_smoke_script "$i"; then
      startup_ok=true
      break
    fi
  else
    log "(dry-run) would run check-baron-api-env.sh and api-smoke.sh"
    startup_ok=true
    break
  fi
  i=$((i + 1))
  if [ "$i" -le "$RETRIES" ]; then
    run_cmd "sleep $SLEEP_BETWEEN"
  fi
done

# 7) Save logs
run_cmd "cd '$BARON_DIR' && $(compose_cmd "logs --no-color") > '$LOG_DIR/baron-compose.log' 2>&1 || true"

if [ "$startup_ok" = false ]; then
  capture_failure_diagnostics "startup-failure"
  log "Startup failed: health checks did not pass after $RETRIES attempt(s)"
  exit 1
fi

log "Startup finished: $(date)"

if [ "$DRY_RUN" = true ]; then
  log "Dry-run mode: no actions were actually performed. Use --auto to run for real."
fi

exit 0
