#!/bin/sh
set -eu

SOURCE_DIR=${POBSYNC_SOURCE_DIR:-$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)}
APP_DIR=${POBSYNC_APP_DIR:-/opt/pobsync/app}
VENV_DIR=${POBSYNC_VENV_DIR:-/opt/pobsync/venv}
ENV_FILE=${POBSYNC_ENV_FILE:-/etc/pobsync/pobsync.env}
SERVICE_USER=${POBSYNC_SERVICE_USER:-pobsync}
SERVICE_GROUP=${POBSYNC_SERVICE_GROUP:-pobsync}
INSTALL_EXTRAS=${POBSYNC_INSTALL_EXTRAS:-}
SERVER_NAME=${POBSYNC_SERVER_NAME:-_}
ALLOWED_HOSTS=${POBSYNC_ALLOWED_HOSTS:-localhost,127.0.0.1}
CSRF_TRUSTED_ORIGINS=${POBSYNC_CSRF_TRUSTED_ORIGINS:-}
BACKUP_ROOT=${POBSYNC_BACKUP_ROOT:-/backups}
BACKUP_ROOT_EXPLICIT=0
if [ -n "${POBSYNC_BACKUP_ROOT:-}" ]; then
    BACKUP_ROOT_EXPLICIT=1
fi
WEB_BIND=${POBSYNC_WEB_BIND:-127.0.0.1:8010}
TIME_ZONE=${POBSYNC_TIME_ZONE:-}
FORCE_ENV=0
INSTALL_OS_PACKAGES=1
WITH_NGINX=0
VERBOSE=0
INTERACTIVE=0
CREATE_SUPERUSER=ask
SUPERUSER_USERNAME=${POBSYNC_SUPERUSER_USERNAME:-}
SUPERUSER_EMAIL=${POBSYNC_SUPERUSER_EMAIL:-}
SUPERUSER_PASSWORD=${POBSYNC_SUPERUSER_PASSWORD:-}

if [ -t 0 ]; then
    INTERACTIVE=1
fi

while [ "$#" -gt 0 ]; do
    case "$1" in
        --source-dir)
            SOURCE_DIR=$2
            shift 2
            ;;
        --app-dir)
            APP_DIR=$2
            shift 2
            ;;
        --venv-dir)
            VENV_DIR=$2
            shift 2
            ;;
        --env-file)
            ENV_FILE=$2
            shift 2
            ;;
        --service-user)
            SERVICE_USER=$2
            shift 2
            ;;
        --service-group)
            SERVICE_GROUP=$2
            shift 2
            ;;
        --backup-root)
            BACKUP_ROOT=$2
            BACKUP_ROOT_EXPLICIT=1
            shift 2
            ;;
        --allowed-hosts)
            ALLOWED_HOSTS=$2
            shift 2
            ;;
        --csrf-trusted-origins)
            CSRF_TRUSTED_ORIGINS=$2
            shift 2
            ;;
        --web-bind)
            WEB_BIND=$2
            shift 2
            ;;
        --time-zone)
            TIME_ZONE=$2
            shift 2
            ;;
        --force-env)
            FORCE_ENV=1
            shift
            ;;
        --verbose)
            VERBOSE=1
            shift
            ;;
        --interactive)
            INTERACTIVE=1
            shift
            ;;
        --non-interactive)
            INTERACTIVE=0
            shift
            ;;
        --no-install-os-packages)
            INSTALL_OS_PACKAGES=0
            shift
            ;;
        --install-extras)
            INSTALL_EXTRAS=$2
            shift 2
            ;;
        --with-nginx)
            WITH_NGINX=1
            shift
            ;;
        --server-name)
            SERVER_NAME=$2
            shift 2
            ;;
        --create-superuser)
            CREATE_SUPERUSER=1
            shift
            ;;
        --no-create-superuser)
            CREATE_SUPERUSER=0
            shift
            ;;
        --superuser-username)
            SUPERUSER_USERNAME=$2
            shift 2
            ;;
        --superuser-email)
            SUPERUSER_EMAIL=$2
            shift 2
            ;;
        --superuser-password)
            SUPERUSER_PASSWORD=$2
            shift 2
            ;;
        *)
            echo "Unknown argument: $1" >&2
            exit 2
            ;;
    esac
done

if [ "$(id -u)" -ne 0 ]; then
    echo "Run this installer as root." >&2
    exit 1
fi

if [ -f "$ENV_FILE" ] && [ "$FORCE_ENV" -ne 1 ] && [ "$BACKUP_ROOT_EXPLICIT" -ne 1 ]; then
    set -a
    # shellcheck disable=SC1090
    . "$ENV_FILE"
    set +a
    if [ -n "${POBSYNC_BACKUP_ROOT:-}" ]; then
        BACKUP_ROOT=$POBSYNC_BACKUP_ROOT
    fi
fi

detect_time_zone() {
    if [ -n "$TIME_ZONE" ]; then
        printf '%s\n' "$TIME_ZONE"
        return
    fi

    if [ -n "${POBSYNC_TIME_ZONE:-}" ]; then
        printf '%s\n' "$POBSYNC_TIME_ZONE"
        return
    fi

    if command -v timedatectl >/dev/null 2>&1; then
        detected=$(timedatectl show -p Timezone --value 2>/dev/null || true)
        if [ -n "$detected" ]; then
            printf '%s\n' "$detected"
            return
        fi
    fi

    if [ -f /etc/timezone ]; then
        detected=$(sed -n '1p' /etc/timezone | tr -d '[:space:]')
        if [ -n "$detected" ]; then
            printf '%s\n' "$detected"
            return
        fi
    fi

    printf 'UTC\n'
}

TIME_ZONE=$(detect_time_zone)

run_step() {
    label=$1
    shift

    if [ "$VERBOSE" -eq 1 ]; then
        echo "==> $label"
        "$@"
        echo "OK: $label"
        return
    fi

    printf '%-48s' "$label"
    log_file=$(mktemp)
    if "$@" >"$log_file" 2>&1; then
        rm -f "$log_file"
        echo "OK"
        return
    fi

    echo "FAILED"
    echo
    echo "Output from failed step '$label':" >&2
    cat "$log_file" >&2
    rm -f "$log_file"
    exit 1
}

note_step() {
    label=$1
    status=$2
    printf '%-48s%s\n' "$label" "$status"
}

prompt_value() {
    prompt=$1
    default=$2
    if [ "$INTERACTIVE" -ne 1 ]; then
        printf '%s\n' "$default"
        return
    fi

    printf '%s [%s]: ' "$prompt" "$default" >&2
    read -r answer
    if [ -n "$answer" ]; then
        printf '%s\n' "$answer"
    else
        printf '%s\n' "$default"
    fi
}

prompt_yes_no() {
    prompt=$1
    default=$2
    if [ "$INTERACTIVE" -ne 1 ]; then
        printf '%s\n' "$default"
        return
    fi

    if [ "$default" -eq 1 ]; then
        suffix=Y/n
    else
        suffix=y/N
    fi

    while :; do
        printf '%s [%s]: ' "$prompt" "$suffix" >&2
        read -r answer
        case "$answer" in
            "")
                printf '%s\n' "$default"
                return
                ;;
            y|Y|yes|YES|Yes)
                printf '1\n'
                return
                ;;
            n|N|no|NO|No)
                printf '0\n'
                return
                ;;
        esac
    done
}

prompt_secret() {
    prompt=$1
    if [ "$INTERACTIVE" -ne 1 ]; then
        printf '\n'
        return
    fi

    printf '%s: ' "$prompt" >&2
    stty -echo
    read -r secret
    stty echo
    printf '\n' >&2
    printf '%s\n' "$secret"
}

if [ "$INTERACTIVE" -eq 1 ]; then
    echo "pobsync native installer"
    echo
    echo "Press Enter to accept defaults. Existing command-line flags are already applied as defaults."
    echo

    SOURCE_DIR=$(prompt_value "Source checkout" "$SOURCE_DIR")
    APP_DIR=$(prompt_value "Install app directory" "$APP_DIR")
    VENV_DIR=$(prompt_value "Python virtualenv directory" "$VENV_DIR")
    ENV_FILE=$(prompt_value "Environment file" "$ENV_FILE")
    SERVICE_USER=$(prompt_value "Service user" "$SERVICE_USER")
    SERVICE_GROUP=$(prompt_value "Service group" "$SERVICE_GROUP")
    BACKUP_ROOT=$(prompt_value "Backup storage path" "$BACKUP_ROOT")
    WEB_BIND=$(prompt_value "Gunicorn bind address" "$WEB_BIND")
    TIME_ZONE=$(prompt_value "Scheduler time zone" "$TIME_ZONE")
    ALLOWED_HOSTS=$(prompt_value "Allowed hosts" "$ALLOWED_HOSTS")
    CSRF_TRUSTED_ORIGINS=$(prompt_value "CSRF trusted origins, comma-separated or blank" "$CSRF_TRUSTED_ORIGINS")
    INSTALL_OS_PACKAGES=$(prompt_yes_no "Install required OS packages with apt-get" "$INSTALL_OS_PACKAGES")

    use_mariadb=0
    if [ "$INSTALL_EXTRAS" = "mariadb" ] || [ "$INSTALL_EXTRAS" = "[mariadb]" ] || [ "$INSTALL_EXTRAS" = ".[mariadb]" ]; then
        use_mariadb=1
    fi
    use_mariadb=$(prompt_yes_no "Install MariaDB Python/client support" "$use_mariadb")
    if [ "$use_mariadb" -eq 1 ]; then
        INSTALL_EXTRAS=mariadb
    else
        INSTALL_EXTRAS=
    fi

    WITH_NGINX=$(prompt_yes_no "Install starter nginx reverse proxy config" "$WITH_NGINX")
    if [ "$WITH_NGINX" -eq 1 ]; then
        SERVER_NAME=$(prompt_value "Nginx server_name" "$SERVER_NAME")
    fi

    if [ "$CREATE_SUPERUSER" = "ask" ]; then
        CREATE_SUPERUSER=$(prompt_yes_no "Create first Django superuser after install" 1)
    fi
    if [ "$CREATE_SUPERUSER" -eq 1 ]; then
        SUPERUSER_USERNAME=$(prompt_value "Superuser username" "${SUPERUSER_USERNAME:-admin}")
        SUPERUSER_EMAIL=$(prompt_value "Superuser email, blank allowed" "$SUPERUSER_EMAIL")
        if [ -z "$SUPERUSER_PASSWORD" ]; then
            SUPERUSER_PASSWORD=$(prompt_secret "Superuser password, leave blank to run createsuperuser interactively later")
        fi
    fi

    echo
fi

if [ "$CREATE_SUPERUSER" = "ask" ]; then
    if [ -n "$SUPERUSER_USERNAME" ] && [ -n "$SUPERUSER_PASSWORD" ]; then
        CREATE_SUPERUSER=1
    else
        CREATE_SUPERUSER=0
    fi
fi

install_os_packages() {
    if [ "$INSTALL_OS_PACKAGES" -ne 1 ]; then
        note_step "Install OS packages" "SKIPPED"
        return
    fi

    if command -v apt-get >/dev/null 2>&1; then
        packages="python3 python3-venv python3-pip rsync openssh-client"
        if [ "$WITH_NGINX" -eq 1 ]; then
            packages="$packages nginx"
        fi
        if [ "$INSTALL_EXTRAS" = "mariadb" ] || [ "$INSTALL_EXTRAS" = "[mariadb]" ] || [ "$INSTALL_EXTRAS" = ".[mariadb]" ]; then
            packages="$packages default-libmysqlclient-dev build-essential pkg-config"
        fi
        run_step "Install OS packages" sh -c "apt-get update && apt-get install -y --no-install-recommends $packages"
        return
    fi

    echo "No supported package manager found; install python3, python3-venv, rsync, and openssh-client manually." >&2
}

install_os_packages

if ! command -v python3 >/dev/null 2>&1; then
    echo "python3 is required." >&2
    exit 1
fi

if ! env POBSYNC_INSTALL_TIME_ZONE="$TIME_ZONE" python3 -c "import os; from zoneinfo import ZoneInfo; ZoneInfo(os.environ['POBSYNC_INSTALL_TIME_ZONE'])" >/dev/null 2>&1; then
    echo "Invalid time zone: $TIME_ZONE" >&2
    echo "Use an IANA timezone such as UTC or Europe/Amsterdam." >&2
    exit 1
fi

if ! command -v rsync >/dev/null 2>&1; then
    echo "rsync is required." >&2
    exit 1
fi

if ! command -v ssh >/dev/null 2>&1; then
    echo "openssh-client is required." >&2
    exit 1
fi

if [ ! -f "$SOURCE_DIR/manage.py" ]; then
    echo "Source directory does not look like a pobsync checkout: $SOURCE_DIR" >&2
    exit 1
fi

if ! getent group "$SERVICE_GROUP" >/dev/null 2>&1; then
    run_step "Create service group" groupadd --system "$SERVICE_GROUP"
else
    note_step "Create service group" "OK"
fi

if ! id "$SERVICE_USER" >/dev/null 2>&1; then
    run_step "Create service user" useradd --system --home /var/lib/pobsync --shell /usr/sbin/nologin --gid "$SERVICE_GROUP" "$SERVICE_USER"
else
    note_step "Create service user" "OK"
fi

grant_journal_access() {
    for group in systemd-journal adm; do
        if getent group "$group" >/dev/null 2>&1; then
            usermod -a -G "$group" "$SERVICE_USER"
        fi
    done
}

run_step "Grant journal access" grant_journal_access
run_step "Prepare directories" mkdir -p /etc/pobsync /var/lib/pobsync /var/log/pobsync "$(dirname "$VENV_DIR")" "$APP_DIR" "$BACKUP_ROOT"
run_step "Set state directory permissions" chown "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync "$BACKUP_ROOT"
run_step "Set private directory modes" chmod 0750 /var/lib/pobsync /var/log/pobsync "$BACKUP_ROOT"

if [ "$SOURCE_DIR" != "$APP_DIR" ]; then
    run_step "Sync application files" rsync -a --delete \
        --exclude .git \
        --exclude .venv \
        --exclude __pycache__ \
        --exclude .pytest_cache \
        --exclude .mypy_cache \
        --exclude var \
        "$SOURCE_DIR"/ "$APP_DIR"/
else
    note_step "Sync application files" "SKIPPED"
fi

run_step "Create Python virtualenv" python3 -m venv "$VENV_DIR"
run_step "Upgrade pip" "$VENV_DIR/bin/python" -m pip install --upgrade pip
case "$INSTALL_EXTRAS" in
    "")
        pip_target=$APP_DIR
        ;;
    mariadb)
        pip_target="$APP_DIR[mariadb]"
        ;;
    \[*\])
        pip_target="$APP_DIR$INSTALL_EXTRAS"
        ;;
    .\[*\])
        pip_target="$APP_DIR${INSTALL_EXTRAS#.}"
        ;;
    *)
        echo "Unsupported install extras: $INSTALL_EXTRAS" >&2
        exit 2
        ;;
esac
run_step "Install Python package" "$VENV_DIR/bin/python" -m pip install -e "$pip_target"

if [ ! -f "$ENV_FILE" ] || [ "$FORCE_ENV" -eq 1 ]; then
    secret=$("$VENV_DIR/bin/python" -c "import secrets; print(secrets.token_urlsafe(48))")
    cat > "$ENV_FILE" <<EOF
POBSYNC_DJANGO_DEBUG=0
POBSYNC_DJANGO_SECRET_KEY=$secret
POBSYNC_DJANGO_ALLOWED_HOSTS=$ALLOWED_HOSTS
POBSYNC_DJANGO_CSRF_TRUSTED_ORIGINS=$CSRF_TRUSTED_ORIGINS

POBSYNC_HOME=/var/lib/pobsync
POBSYNC_BACKUP_ROOT=$BACKUP_ROOT
POBSYNC_TIME_ZONE=$TIME_ZONE
POBSYNC_SQLITE_PATH=/var/lib/pobsync/pobsync.sqlite3
POBSYNC_STATIC_ROOT=/var/lib/pobsync/static
POBSYNC_ENV_FILE=$ENV_FILE
POBSYNC_SERVICE_USER=$SERVICE_USER
POBSYNC_SERVICE_GROUP=$SERVICE_GROUP

POBSYNC_WEB_BIND=$WEB_BIND
POBSYNC_GUNICORN_WORKERS=2
POBSYNC_GUNICORN_TIMEOUT=120
POBSYNC_WORKER_INTERVAL=15
POBSYNC_SCHEDULER_INTERVAL=60
POBSYNC_UPDATE_RELEASES_URL=
POBSYNC_UPDATE_RELEASES_TOKEN=
POBSYNC_UPDATE_GIT_REMOTE=origin
POBSYNC_UPDATE_COMMAND=sudo -n scripts/update-systemd
EOF
    chmod 0640 "$ENV_FILE"
    chown "root:$SERVICE_GROUP" "$ENV_FILE"
    note_step "Write environment file" "OK"
else
    note_step "Write environment file" "SKIPPED"
    echo "Keeping existing $ENV_FILE. Use --force-env to rewrite it."
fi

set -a
# shellcheck disable=SC1090
. "$ENV_FILE"
set +a

install_unit() {
    src=$1
    dest=$2
    sed \
        -e "s|@POBSYNC_APP_DIR@|$APP_DIR|g" \
        -e "s|@POBSYNC_VENV_DIR@|$VENV_DIR|g" \
        -e "s|@POBSYNC_ENV_FILE@|$ENV_FILE|g" \
        -e "s|@POBSYNC_USER@|$SERVICE_USER|g" \
        -e "s|@POBSYNC_GROUP@|$SERVICE_GROUP|g" \
        "$src" > "$dest"
    chmod 0644 "$dest"
}

install_units() {
    install_unit "$APP_DIR/deploy/systemd/pobsync-web.service" /etc/systemd/system/pobsync-web.service
    install_unit "$APP_DIR/deploy/systemd/pobsync-worker.service" /etc/systemd/system/pobsync-worker.service
    install_unit "$APP_DIR/deploy/systemd/pobsync-scheduler.service" /etc/systemd/system/pobsync-scheduler.service
}

run_step "Install systemd units" install_units

install_manage_wrapper() {
    sed \
        -e "s|@POBSYNC_APP_DIR@|$APP_DIR|g" \
        -e "s|@POBSYNC_VENV_DIR@|$VENV_DIR|g" \
        -e "s|@POBSYNC_ENV_FILE@|$ENV_FILE|g" \
        -e "s|@POBSYNC_USER@|$SERVICE_USER|g" \
        -e "s|@POBSYNC_GROUP@|$SERVICE_GROUP|g" \
        "$APP_DIR/deploy/bin/pobsync-manage" > /usr/local/bin/pobsync-manage
    chmod 0755 /usr/local/bin/pobsync-manage
}

run_step "Install manage wrapper" install_manage_wrapper

run_step "Reload systemd" systemctl daemon-reload
run_step "Run database migrations" /usr/local/bin/pobsync-manage migrate --noinput
run_step "Ensure default SSH key" /usr/local/bin/pobsync-manage ensure_pobsync_ssh_key --name default --set-global-default
run_step "Collect static files" /usr/local/bin/pobsync-manage collectstatic --noinput --clear
run_step "Finalize state permissions" chown -R "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync

superuser_exists=$("$VENV_DIR/bin/python" -c "import os; os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'pobsync_server.settings'); import django; django.setup(); from django.contrib.auth import get_user_model; print('yes' if get_user_model().objects.filter(is_superuser=True).exists() else 'no')")
if [ "$CREATE_SUPERUSER" -eq 1 ]; then
    if [ "$superuser_exists" = "yes" ]; then
        note_step "Create Django superuser" "SKIPPED"
    elif [ -n "$SUPERUSER_USERNAME" ] && [ -n "$SUPERUSER_PASSWORD" ]; then
        run_step "Create Django superuser" env \
            DJANGO_SUPERUSER_USERNAME="$SUPERUSER_USERNAME" \
            DJANGO_SUPERUSER_EMAIL="$SUPERUSER_EMAIL" \
            DJANGO_SUPERUSER_PASSWORD="$SUPERUSER_PASSWORD" \
            /usr/local/bin/pobsync-manage createsuperuser --noinput
        run_step "Finalize superuser permissions" chown -R "$SERVICE_USER:$SERVICE_GROUP" /var/lib/pobsync /var/log/pobsync
    else
        note_step "Create Django superuser" "SKIPPED"
        echo "No superuser password was provided; create one later with:"
        echo "  sudo -u $SERVICE_USER pobsync-manage createsuperuser"
    fi
elif [ "$superuser_exists" != "yes" ]; then
    note_step "Create Django superuser" "SKIPPED"
    echo "No Django superuser exists yet. Create one with:"
    echo "  sudo -u $SERVICE_USER pobsync-manage createsuperuser"
else
    note_step "Create Django superuser" "SKIPPED"
fi

run_step "Enable services" systemctl enable pobsync-web.service pobsync-worker.service pobsync-scheduler.service
run_step "Restart services" systemctl restart pobsync-web.service pobsync-worker.service pobsync-scheduler.service

if [ "$WITH_NGINX" -eq 1 ]; then
    if ! command -v nginx >/dev/null 2>&1; then
        note_step "Install nginx config" "SKIPPED"
        echo "nginx is not installed; skipping nginx config." >&2
    else
        sed "s|@POBSYNC_SERVER_NAME@|$SERVER_NAME|g" "$APP_DIR/deploy/nginx/pobsync.conf" > /etc/nginx/sites-available/pobsync.conf
        ln -sf /etc/nginx/sites-available/pobsync.conf /etc/nginx/sites-enabled/pobsync.conf
        note_step "Install nginx config" "OK"
        run_step "Validate nginx config" nginx -t
        run_step "Reload nginx" systemctl reload nginx
    fi
else
    note_step "Install nginx config" "SKIPPED"
fi

if [ "$VERBOSE" -eq 1 ]; then
    systemctl --no-pager --full status pobsync-web.service pobsync-worker.service pobsync-scheduler.service || true
fi

echo
echo "pobsync installation complete."
echo
echo "Open the Django control panel at:"
echo "  http://$WEB_BIND/"
echo
echo "If pobsync is behind a reverse proxy, use your public hostname instead."
echo
echo "Recommended first steps:"
echo "  1. Log in to the Django control panel."
echo "  2. Open Self Check and resolve any warnings."
echo "  3. Configure global settings and backup storage."
echo "  4. Add an SSH key under SSH Keys."
echo "  5. Add a host and queue a dry-run backup."
echo
echo "Useful commands:"
echo "  systemctl status pobsync-web pobsync-worker pobsync-scheduler"
echo "  journalctl -u pobsync-worker -f"
echo "  sudo -u $SERVICE_USER pobsync-manage check"
echo "  sudo -u $SERVICE_USER pobsync-manage check_pobsync_install"
