#!/usr/bin/env sh
set -eu

if (set -o pipefail) >/dev/null 2>&1; then
    set -o pipefail
fi

export MSYS_NO_PATHCONV=1

SCRIPT_PATH="$0"
case "$SCRIPT_PATH" in
    /*) ;;
    *) SCRIPT_PATH="$(pwd)/$SCRIPT_PATH" ;;
esac
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$SCRIPT_PATH")" && pwd)"
cd "$SCRIPT_DIR"
ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
ROOT_ENV_FILE="$ROOT_DIR/.env"
ROOT_ENV_EXAMPLE_FILE="$ROOT_DIR/.env.example"
MODE="${1:-start}"

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

info()  { printf '%b\n' "${GREEN}[INFO]${NC} $*"; }
warn()  { printf '%b\n' "${YELLOW}[WARN]${NC} $*"; }
error() { printf '%b\n' "${RED}[ERROR]${NC} $*"; exit 1; }

is_container() {
    [ -f "/.dockerenv" ] && return 0

    if [ -r /proc/1/cgroup ] && grep -Eq "(docker|containerd|kubepods)" /proc/1/cgroup 2>/dev/null; then
        return 0
    fi

    return 1
}

is_wsl() {
    grep -qi microsoft /proc/version 2>/dev/null
}

is_msys() {
    case "$(uname -s)" in
        MINGW*|MSYS*|CYGWIN*) return 0 ;;
        *) return 1 ;;
    esac
}

to_windows_path() {
    target_path="$1"

    if is_wsl && command -v wslpath >/dev/null 2>&1; then
        wslpath -w "$target_path"
        return 0
    fi

    if is_msys && command -v cygpath >/dev/null 2>&1; then
        cygpath -aw "$target_path"
        return 0
    fi

    printf '%s\n' "$target_path"
}

DEFAULT_VENV_DIR="$SCRIPT_DIR/.venv"
DEFAULT_VENV_DISPLAY_PATH="./server/.venv"
if [ -n "${SERVER_VENV_DIR:-}" ]; then
    VENV_DIR="$SERVER_VENV_DIR"
    VENV_DISPLAY_PATH="$SERVER_VENV_DIR"
elif is_container; then
    VENV_DIR="/tmp/x-financial-server-venv"
    VENV_DISPLAY_PATH="$VENV_DIR"
else
    VENV_DIR="$DEFAULT_VENV_DIR"
    VENV_DISPLAY_PATH="$DEFAULT_VENV_DISPLAY_PATH"
fi

if [ ! -f "$ROOT_ENV_FILE" ]; then
    if [ -f "$ROOT_ENV_EXAMPLE_FILE" ]; then
        warn "Root .env not found. Creating it from .env.example"
        cp "$ROOT_ENV_EXAMPLE_FILE" "$ROOT_ENV_FILE"
    else
        error "Root .env and .env.example are both missing."
    fi
fi

ENV_OVERRIDE_SERVER_HOST_SET=false
ENV_OVERRIDE_SERVER_PORT_SET=false
ENV_OVERRIDE_POSTGRES_HOST_SET=false
ENV_OVERRIDE_POSTGRES_PORT_SET=false
ENV_OVERRIDE_POSTGRES_DB_SET=false
ENV_OVERRIDE_POSTGRES_USER_SET=false
ENV_OVERRIDE_POSTGRES_PASSWORD_SET=false
ENV_OVERRIDE_DATABASE_URL_SET=false
ENV_OVERRIDE_STARTUP_BOOTSTRAP_ENABLED_SET=false
ENV_OVERRIDE_BACKGROUND_SCHEDULERS_ENABLED_SET=false
ENV_OVERRIDE_ONLYOFFICE_ENABLED_SET=false
ENV_OVERRIDE_ONLYOFFICE_PUBLIC_URL_SET=false
ENV_OVERRIDE_ONLYOFFICE_BACKEND_URL_SET=false
ENV_OVERRIDE_ONLYOFFICE_JWT_SECRET_SET=false
PREFER_ENV_FILE_FOR_ONLYOFFICE=false

case "${X_FINANCIAL_PREFER_ENV_FILE:-false}" in
    1|true|TRUE|yes|YES|on|ON)
        PREFER_ENV_FILE_FOR_ONLYOFFICE=true
        ;;
esac

if [ "${SERVER_HOST+x}" = x ]; then
    ENV_OVERRIDE_SERVER_HOST_SET=true
    ENV_OVERRIDE_SERVER_HOST="$SERVER_HOST"
fi

if [ "${SERVER_PORT+x}" = x ]; then
    ENV_OVERRIDE_SERVER_PORT_SET=true
    ENV_OVERRIDE_SERVER_PORT="$SERVER_PORT"
fi

if [ "${POSTGRES_HOST+x}" = x ]; then
    ENV_OVERRIDE_POSTGRES_HOST_SET=true
    ENV_OVERRIDE_POSTGRES_HOST="$POSTGRES_HOST"
fi

if [ "${POSTGRES_PORT+x}" = x ]; then
    ENV_OVERRIDE_POSTGRES_PORT_SET=true
    ENV_OVERRIDE_POSTGRES_PORT="$POSTGRES_PORT"
fi

if [ "${POSTGRES_DB+x}" = x ]; then
    ENV_OVERRIDE_POSTGRES_DB_SET=true
    ENV_OVERRIDE_POSTGRES_DB="$POSTGRES_DB"
fi

if [ "${POSTGRES_USER+x}" = x ]; then
    ENV_OVERRIDE_POSTGRES_USER_SET=true
    ENV_OVERRIDE_POSTGRES_USER="$POSTGRES_USER"
fi

if [ "${POSTGRES_PASSWORD+x}" = x ]; then
    ENV_OVERRIDE_POSTGRES_PASSWORD_SET=true
    ENV_OVERRIDE_POSTGRES_PASSWORD="$POSTGRES_PASSWORD"
fi

if [ "${DATABASE_URL+x}" = x ]; then
    ENV_OVERRIDE_DATABASE_URL_SET=true
    ENV_OVERRIDE_DATABASE_URL="$DATABASE_URL"
fi

if [ "${STARTUP_BOOTSTRAP_ENABLED+x}" = x ]; then
    ENV_OVERRIDE_STARTUP_BOOTSTRAP_ENABLED_SET=true
    ENV_OVERRIDE_STARTUP_BOOTSTRAP_ENABLED="$STARTUP_BOOTSTRAP_ENABLED"
fi

if [ "${BACKGROUND_SCHEDULERS_ENABLED+x}" = x ]; then
    ENV_OVERRIDE_BACKGROUND_SCHEDULERS_ENABLED_SET=true
    ENV_OVERRIDE_BACKGROUND_SCHEDULERS_ENABLED="$BACKGROUND_SCHEDULERS_ENABLED"
fi

if [ "$PREFER_ENV_FILE_FOR_ONLYOFFICE" != true ] && [ "${ONLYOFFICE_ENABLED+x}" = x ]; then
    ENV_OVERRIDE_ONLYOFFICE_ENABLED_SET=true
    ENV_OVERRIDE_ONLYOFFICE_ENABLED="$ONLYOFFICE_ENABLED"
fi

if [ "$PREFER_ENV_FILE_FOR_ONLYOFFICE" != true ] && [ "${ONLYOFFICE_PUBLIC_URL+x}" = x ]; then
    ENV_OVERRIDE_ONLYOFFICE_PUBLIC_URL_SET=true
    ENV_OVERRIDE_ONLYOFFICE_PUBLIC_URL="$ONLYOFFICE_PUBLIC_URL"
fi

if [ "$PREFER_ENV_FILE_FOR_ONLYOFFICE" != true ] && [ "${ONLYOFFICE_BACKEND_URL+x}" = x ]; then
    ENV_OVERRIDE_ONLYOFFICE_BACKEND_URL_SET=true
    ENV_OVERRIDE_ONLYOFFICE_BACKEND_URL="$ONLYOFFICE_BACKEND_URL"
fi

if [ "$PREFER_ENV_FILE_FOR_ONLYOFFICE" != true ] && [ "${ONLYOFFICE_JWT_SECRET+x}" = x ]; then
    ENV_OVERRIDE_ONLYOFFICE_JWT_SECRET_SET=true
    ENV_OVERRIDE_ONLYOFFICE_JWT_SECRET="$ONLYOFFICE_JWT_SECRET"
fi

set -a
. "$ROOT_ENV_FILE"
set +a

if [ "$ENV_OVERRIDE_SERVER_HOST_SET" = true ]; then
    SERVER_HOST="$ENV_OVERRIDE_SERVER_HOST"
fi

if [ "$ENV_OVERRIDE_SERVER_PORT_SET" = true ]; then
    SERVER_PORT="$ENV_OVERRIDE_SERVER_PORT"
fi

if [ "$ENV_OVERRIDE_POSTGRES_HOST_SET" = true ]; then
    POSTGRES_HOST="$ENV_OVERRIDE_POSTGRES_HOST"
fi

if [ "$ENV_OVERRIDE_POSTGRES_PORT_SET" = true ]; then
    POSTGRES_PORT="$ENV_OVERRIDE_POSTGRES_PORT"
fi

if [ "$ENV_OVERRIDE_POSTGRES_DB_SET" = true ]; then
    POSTGRES_DB="$ENV_OVERRIDE_POSTGRES_DB"
fi

if [ "$ENV_OVERRIDE_POSTGRES_USER_SET" = true ]; then
    POSTGRES_USER="$ENV_OVERRIDE_POSTGRES_USER"
fi

if [ "$ENV_OVERRIDE_POSTGRES_PASSWORD_SET" = true ]; then
    POSTGRES_PASSWORD="$ENV_OVERRIDE_POSTGRES_PASSWORD"
fi

if [ "$ENV_OVERRIDE_DATABASE_URL_SET" = true ]; then
    DATABASE_URL="$ENV_OVERRIDE_DATABASE_URL"
fi

if [ "$ENV_OVERRIDE_STARTUP_BOOTSTRAP_ENABLED_SET" = true ]; then
    STARTUP_BOOTSTRAP_ENABLED="$ENV_OVERRIDE_STARTUP_BOOTSTRAP_ENABLED"
fi

if [ "$ENV_OVERRIDE_BACKGROUND_SCHEDULERS_ENABLED_SET" = true ]; then
    BACKGROUND_SCHEDULERS_ENABLED="$ENV_OVERRIDE_BACKGROUND_SCHEDULERS_ENABLED"
fi

if [ "$ENV_OVERRIDE_ONLYOFFICE_ENABLED_SET" = true ]; then
    ONLYOFFICE_ENABLED="$ENV_OVERRIDE_ONLYOFFICE_ENABLED"
fi

if [ "$ENV_OVERRIDE_ONLYOFFICE_PUBLIC_URL_SET" = true ]; then
    ONLYOFFICE_PUBLIC_URL="$ENV_OVERRIDE_ONLYOFFICE_PUBLIC_URL"
fi

if [ "$ENV_OVERRIDE_ONLYOFFICE_BACKEND_URL_SET" = true ]; then
    ONLYOFFICE_BACKEND_URL="$ENV_OVERRIDE_ONLYOFFICE_BACKEND_URL"
fi

if [ "$ENV_OVERRIDE_ONLYOFFICE_JWT_SECRET_SET" = true ]; then
    ONLYOFFICE_JWT_SECRET="$ENV_OVERRIDE_ONLYOFFICE_JWT_SECRET"
fi

SERVER_HOST="${SERVER_HOST:-0.0.0.0}"
SERVER_PORT="${SERVER_PORT:-8000}"
DEFAULT_SERVER_RELOAD="false"

case "${APP_ENV:-local}" in
    local|dev|development)
        DEFAULT_SERVER_RELOAD="true"
        ;;
esac

if [ "${APP_DEBUG:-true}" = "true" ]; then
    DEFAULT_SERVER_RELOAD="true"
fi

if is_container; then
    DEFAULT_SERVER_RELOAD="false"
fi

SERVER_RELOAD="${SERVER_RELOAD:-$DEFAULT_SERVER_RELOAD}"
SERVER_WORKERS="${SERVER_WORKERS:-${WEB_CONCURRENCY:-1}}"
export SERVER_WORKERS

needs_windows_python() {
    is_msys || is_wsl
}

find_unix_python() {
    if command -v python3 >/dev/null 2>&1; then
        echo "python3"
        return 0
    fi

    if command -v python >/dev/null 2>&1; then
        echo "python"
        return 0
    fi

    return 1
}

find_windows_python() {
    if command -v py.exe >/dev/null 2>&1; then
        echo "py.exe -3"
        return 0
    fi

    if command -v python.exe >/dev/null 2>&1; then
        echo "python.exe"
        return 0
    fi

    return 1
}

venv_python_path() {
    if [ -x "$VENV_DIR/Scripts/python.exe" ]; then
        echo "$VENV_DIR/Scripts/python.exe"
        return 0
    fi

    if [ -x "$VENV_DIR/bin/python" ]; then
        echo "$VENV_DIR/bin/python"
        return 0
    fi

    return 1
}

run_bootstrap_python() {
    $PYTHON_BOOTSTRAP "$@"
}

dependencies_ready() {
    "$PYTHON_BIN" -c "import alembic, dotenv, email_validator, fastapi, jieba, jwt, lightrag, multipart, openpyxl, psycopg, pydantic_settings, qdrant_client, sqlalchemy, uvicorn" >/dev/null 2>&1
}

pip_ready() {
    "$PYTHON_BIN" -m pip --version >/dev/null 2>&1
}

create_venv() {
    info "Creating virtual environment at $VENV_DISPLAY_PATH"
    if [ -d "$VENV_DIR" ]; then
        rm -rf "$VENV_DIR"
    fi
    mkdir -p "$(dirname "$VENV_DIR")"

    venv_target="$VENV_DIR"
    if [ "$PYTHON_BOOTSTRAP_IS_WINDOWS" = "true" ]; then
        venv_target="$(to_windows_path "$VENV_DIR")"
    fi

    run_bootstrap_python -m venv "$venv_target"

    if ! PYTHON_BIN="$(venv_python_path)"; then
        error "Virtual environment was not created successfully."
    fi
}

ensure_pip() {
    if pip_ready; then
        return 0
    fi

    warn "pip is missing in .venv, attempting repair"

    if "$PYTHON_BIN" -m ensurepip --upgrade >/dev/null 2>&1 && pip_ready; then
        info "pip restored successfully"
        return 0
    fi

    warn "Recreating .venv because pip repair failed"
    rm -rf "$VENV_DIR"
    create_venv

    if "$PYTHON_BIN" -m ensurepip --upgrade >/dev/null 2>&1 && pip_ready; then
        info "pip restored after recreating .venv"
        return 0
    fi

    error "pip could not be created inside .venv. Install Python with venv and pip support, then rerun the script."
}

ensure_python_bootstrap() {
    PYTHON_BOOTSTRAP_IS_WINDOWS="false"

    if needs_windows_python; then
        if find_windows_python >/dev/null 2>&1; then
            PYTHON_BOOTSTRAP="$(find_windows_python)"
            PYTHON_BOOTSTRAP_IS_WINDOWS="true"
            info "Detected Windows bash environment, using Windows Python"
            return 0
        fi

        if find_unix_python >/dev/null 2>&1; then
            PYTHON_BOOTSTRAP="$(find_unix_python)"
            warn "Windows Python not found, falling back to system Python"
            return 0
        fi

        error "Python is not available in PATH."
    fi

    if ! PYTHON_BOOTSTRAP="$(find_unix_python)"; then
        error "Python is not installed or not available in PATH. Install Python 3.11+ first so the script can create server/.venv automatically."
    fi
}

ensure_dependencies() {
    ensure_python_bootstrap

    if is_container && [ "$VENV_DIR" != "$DEFAULT_VENV_DIR" ]; then
        info "Docker runtime detected, using isolated Python environment at $VENV_DISPLAY_PATH"
    fi

    if ! PYTHON_BIN="$(venv_python_path)"; then
        warn "Python virtual environment not found"
        create_venv
    fi

    ensure_pip

    if dependencies_ready; then
        info "Server dependencies are ready."
        return 0
    fi

    warn "Server dependencies are missing or incomplete"
    info "Running .venv Python dependency installation"
    "$PYTHON_BIN" -m pip install --upgrade pip
    "$PYTHON_BIN" -m pip install -e ".[dev]"

    if ! dependencies_ready; then
        error "Server dependencies are still incomplete after installation."
    fi

    info "Server dependencies are ready."
}

run_database_migrations() {
    info "Checking database migration state..."
    PYTHONPATH="$SCRIPT_DIR/src${PYTHONPATH:+:$PYTHONPATH}" \
        "$PYTHON_BIN" -m app.db.migration_preflight
    info "Applying database migrations..."
    "$PYTHON_BIN" -m alembic -c "$SCRIPT_DIR/alembic.ini" upgrade head
    info "Database migrations are up to date."
}

start_server() {
    info "Starting FastAPI server..."
    info "Access: http://$SERVER_HOST:$SERVER_PORT"
    echo ""

    if [ "$SERVER_RELOAD" = "true" ]; then
        exec "$PYTHON_BIN" -m uvicorn app.main:app --reload --app-dir src --host "$SERVER_HOST" --port "$SERVER_PORT"
    fi

    if [ "$SERVER_WORKERS" -gt 1 ] 2>/dev/null; then
        BACKGROUND_SCHEDULERS_ENABLED="${BACKGROUND_SCHEDULERS_ENABLED:-false}"
        export BACKGROUND_SCHEDULERS_ENABLED
        exec "$PYTHON_BIN" -m uvicorn app.main:app --app-dir src --host "$SERVER_HOST" --port "$SERVER_PORT" --workers "$SERVER_WORKERS"
    fi

    exec "$PYTHON_BIN" -m uvicorn app.main:app --app-dir src --host "$SERVER_HOST" --port "$SERVER_PORT"
}

case "$MODE" in
    deps)
        ensure_dependencies
        ;;
    start)
        ensure_dependencies
        run_database_migrations
        start_server
        ;;
    *)
        error "Unknown mode: $MODE. Use one of: deps, start"
        ;;
esac
