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

# tennisintehran.com — deploy on shared cPanel (same host as i-phone.ir)
#
# Usage (from app root, e.g. ~/tennisintehran.com):
#   chmod +x dep.sh
#   ./dep.sh
#
# Prepare .env once (copy from .env.example, set DB_*), then dep.sh handles the rest.
# APP_KEY is written automatically — no need to run key:generate manually.
#
# Optional env:
#   DEP_BRANCH=main        (production branch — default)
#   PHP_BIN=php            (default: php)
#   SKIP_ENV_WIZARD=1      (never prompt; fail if DB_* missing in .env)

ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
cd "$ROOT"

BRANCH="${DEP_BRANCH:-main}"
PHP_BIN="${PHP_BIN:-php}"
MARKER="${ROOT}/storage/.deploy-initialized"

# ── helpers ──────────────────────────────────────────────────────────────────

info()  { echo "==> $*"; }
warn()  { echo "WARN: $*" >&2; }
die()   { echo "ERROR: $*" >&2; exit 1; }

find_composer() {
    if command -v composer >/dev/null 2>&1; then
        COMPOSER_BIN="composer"
    elif [[ -f "${HOME}/bin/composer.phar" ]]; then
        COMPOSER_BIN="${PHP_BIN} ${HOME}/bin/composer.phar"
    elif [[ -f "${HOME}/ketabeman.com/composer.phar" ]]; then
        COMPOSER_BIN="${PHP_BIN} ${HOME}/ketabeman.com/composer.phar"
    elif [[ -f "${ROOT}/composer.phar" ]]; then
        COMPOSER_BIN="${PHP_BIN} ${ROOT}/composer.phar"
    else
        die "composer not found. Install ~/bin/composer.phar or copy from ketabeman.com."
    fi
}

ensure_git() {
    command -v git >/dev/null 2>&1 || die "git is required."
    [[ -d .git ]] || die "Not a git repo. Clone first, e.g.:
  cd ~ && git clone <repo-url> tennisintehran.com && cd tennisintehran.com"
}

ensure_php() {
    command -v "$PHP_BIN" >/dev/null 2>&1 || die "PHP not found: ${PHP_BIN}"
    local ver
    ver="$("$PHP_BIN" -r 'echo PHP_MAJOR_VERSION.".".PHP_MINOR_VERSION;')"
    info "PHP ${ver}"
}

ensure_writable_dirs() {
    mkdir -p storage/framework/{cache,sessions,views} storage/logs bootstrap/cache
    chmod -R ug+rwx storage bootstrap/cache 2>/dev/null || true
}

verify_build_assets() {
    if [[ -f public/build/manifest.json ]]; then
        info "Frontend assets OK (public/build/manifest.json)"
        return 0
    fi
    die "Missing public/build/manifest.json — server cannot run npm build.
Build on your Mac/CI, commit public/build/, then pull again:
  npm ci && npm run build
  git add public/build && git commit -m \"build assets\" && git push"
}

# Read a value from .env (handles quotes, whitespace, duplicate keys, CRLF).
env_get() {
    local key="$1" line val result=""
    [[ -f .env ]] || return 1
    while IFS= read -r line || [[ -n "$line" ]]; do
        line="${line//$'\r'/}"
        [[ "$line" =~ ^[[:space:]]*# ]] && continue
        [[ "$line" =~ ^[[:space:]]*${key}[[:space:]]*= ]] || continue
        val="${line#*=}"
        val="${val#"${val%%[![:space:]]*}"}"
        val="${val%"${val##*[![:space:]]}"}"
        case "$val" in
            \"*\" ) val="${val:1:${#val}-2}" ;;
            \'*\' ) val="${val:1:${#val}-2}" ;;
        esac
        [[ -n "$val" && "$val" != "null" ]] && result="$val"
    done < .env
    [[ -n "$result" ]] || return 1
    printf '%s' "$result"
}

env_has_key() {
    local key_val
    key_val="$(env_get APP_KEY)" || return 1
    [[ -n "$key_val" ]]
}

db_is_configured() {
    [[ -f .env ]] || return 1
    [[ -n "$(env_get DB_DATABASE)" ]] || return 1
    [[ -n "$(env_get DB_USERNAME)" ]] || return 1
    [[ -n "$(env_get DB_PASSWORD)" ]] || return 1
    return 0
}

env_report_missing() {
    [[ -f .env ]] || warn "No .env file in ${ROOT}"
    [[ -n "$(env_get DB_DATABASE)" ]] || warn "DB_DATABASE is missing or empty"
    [[ -n "$(env_get DB_USERNAME)" ]] || warn "DB_USERNAME is missing or empty"
    [[ -n "$(env_get DB_PASSWORD)" ]] || warn "DB_PASSWORD is missing or empty"
}

# Write KEY=value without sed (passwords often contain &, /, $, etc.)
set_env_var() {
    local key="$1" val="$2" file=".env"
    local tmp found=0 line

    tmp="$(mktemp "${file}.XXXXXX")"
    if [[ -f "$file" ]]; then
        while IFS= read -r line || [[ -n "$line" ]]; do
            line="${line//$'\r'/}"
            if [[ "$line" =~ ^[[:space:]]*${key}[[:space:]]*= ]]; then
                printf '%s=%s\n' "$key" "$val" >>"$tmp"
                found=1
            else
                printf '%s\n' "$line" >>"$tmp"
            fi
        done <"$file"
    fi
    if [[ "$found" -eq 0 ]]; then
        printf '%s=%s\n' "$key" "$val" >>"$tmp"
    fi
    mv "$tmp" "$file"
}

# Quote .env values that contain characters Dotenv treats specially.
quote_env_value() {
    local val="$1"
    if [[ "$val" =~ [\ \#\"\$\\] ]]; then
        val="${val//\\/\\\\}"
        val="${val//\"/\\\"}"
        printf '"%s"' "$val"
    else
        printf '%s' "$val"
    fi
}

ensure_env_file() {
    if [[ -f .env ]]; then
        [[ -w .env ]] || die ".env is not writable: ${ROOT}/.env"
        return 0
    fi
    [[ -f .env.example ]] || die "Missing .env and .env.example"
    cp .env.example .env
    info "Created .env from .env.example"
}

apply_production_defaults() {
    set_env_var "APP_ENV" "production"
    set_env_var "APP_DEBUG" "false"
    set_env_var "APP_TIMEZONE" "Asia/Tehran"
    set_env_var "APP_LOCALE" "fa"
    set_env_var "APP_FALLBACK_LOCALE" "en"
    set_env_var "DB_CONNECTION" "mysql"
    set_env_var "DB_HOST" "127.0.0.1"
    set_env_var "DB_PORT" "3306"
    set_env_var "SESSION_DRIVER" "database"
    set_env_var "CACHE_STORE" "database"
    set_env_var "QUEUE_CONNECTION" "database"
    set_env_var "LOG_CHANNEL" "stack"
    set_env_var "LOG_LEVEL" "warning"
}

# Write APP_KEY directly — works even before composer install.
ensure_app_key() {
    if env_has_key; then
        info "APP_KEY OK"
        return 0
    fi

    info "Generating APP_KEY"

    if [[ -f vendor/autoload.php ]]; then
        $PHP_BIN artisan key:generate --force --no-interaction >/dev/null 2>&1 || true
        if env_has_key; then
            info "APP_KEY set via artisan"
            return 0
        fi
        warn "artisan key:generate did not update .env — writing key directly"
    fi

    local key="base64:$($PHP_BIN -r 'echo base64_encode(random_bytes(32));')"
    set_env_var "APP_KEY" "$key"

    if env_has_key; then
        info "APP_KEY written to .env"
        return 0
    fi

    die "Could not write APP_KEY to .env — check permissions on ${ROOT}/.env"
}

prompt_default() {
    local prompt="$1" default="$2" var
    read -rp "${prompt} [${default}]: " var
    echo "${var:-$default}"
}

prompt_secret() {
    local prompt="$1" __var_name="$2" input
    read -rsp "${prompt}: " input
    echo
    printf -v "$__var_name" '%s' "$input"
}

# Interactive DB setup — only when DB_* missing and stdin is a TTY.
run_env_wizard() {
    info "Database setup — .env wizard (APP_KEY is handled automatically)"

    local app_name app_url db_name db_user db_pass
    app_name="$(prompt_default "APP_NAME" "تنیس در تهران")"
    app_url="$(prompt_default "APP_URL (https, no trailing slash)" "https://tennisintehran.com")"

    cat <<'MYSQL'

MySQL (cPanel → MySQL Databases):
  • Create database + user, add user to database (ALL PRIVILEGES)
  • Use the cPanel-prefixed names, e.g. cpaneluser_tennisintehran
  • Host is usually 127.0.0.1 (not localhost on some hosts)

MYSQL

    db_name="$(prompt_default "DB_DATABASE" "tennisintehran")"
    db_user="$(prompt_default "DB_USERNAME" "tennisintehran")"
    prompt_secret "DB_PASSWORD" db_pass

    set_env_var "APP_NAME" "$(quote_env_value "$app_name")"
    set_env_var "APP_URL" "${app_url}"
    set_env_var "DB_DATABASE" "${db_name}"
    set_env_var "DB_USERNAME" "${db_user}"
    set_env_var "DB_PASSWORD" "$(quote_env_value "$db_pass")"
    apply_production_defaults
}

ensure_db_config() {
    ensure_env_file

    if db_is_configured; then
        apply_production_defaults
        info ".env database settings OK — skipping wizard"
        return 0
    fi

    if [[ "${SKIP_ENV_WIZARD:-}" == "1" ]]; then
        env_report_missing
        die "Set DB_DATABASE, DB_USERNAME, and DB_PASSWORD in .env, then re-run ./dep.sh"
    fi

    if [[ -t 0 ]]; then
        warn "Database credentials missing in .env"
        env_report_missing
        run_env_wizard
        db_is_configured || die "Database credentials still missing after wizard"
        return 0
    fi

    env_report_missing
    die "Database credentials missing in .env (non-interactive shell). Edit .env and re-run."
}

is_yes() {
    case "${1:-}" in
        y|Y|yes|Yes|YES) return 0 ;;
        *) return 1 ;;
    esac
}

maybe_seed() {
    if [[ -f "$MARKER" ]]; then
        return 0
    fi
    read -rp "Run database seeders (regions + sample data)? [y/N]: " ans
    if is_yes "$ans"; then
        info "Seeding database"
        $PHP_BIN artisan db:seed --force
    fi
    maybe_create_admin
}

maybe_create_admin() {
    read -rp "Create Filament admin user now? [y/N]: " ans
    if is_yes "$ans"; then
        $PHP_BIN artisan make:filament-user
    else
        echo "    Later: php artisan make:filament-user"
    fi
}

# ── deploy steps ─────────────────────────────────────────────────────────────

git_pull() {
    info "Fetching latest code (branch: ${BRANCH})"
    git fetch origin
    git checkout "$BRANCH"
    git pull --ff-only origin "$BRANCH"
    info "At commit $(git rev-parse --short HEAD) — $(git log -1 --pretty=%s)"
}

composer_install() {
    info "Installing Composer dependencies (--no-dev)"
    # shellcheck disable=SC2086
    $COMPOSER_BIN install --no-dev --prefer-dist --optimize-autoloader --no-interaction
}

artisan_migrate() {
    info "Running migrations"
    $PHP_BIN artisan migrate --force
}

artisan_storage_link() {
    info "Storage link"
    $PHP_BIN artisan storage:link 2>/dev/null || warn "storage:link failed — link or copy storage/app/public manually in cPanel."
}

clear_and_cache() {
    info "Clearing caches"
    $PHP_BIN artisan optimize:clear

    info "Rebuilding production caches"
    $PHP_BIN artisan config:cache
    $PHP_BIN artisan route:cache
    $PHP_BIN artisan view:cache
    $PHP_BIN artisan event:cache 2>/dev/null || true
    $PHP_BIN artisan filament:optimize 2>/dev/null || true
}

queue_restart() {
    info "Restarting queue workers (no-op if queue is sync)"
    $PHP_BIN artisan queue:restart || true
}

mark_initialized() {
    touch "$MARKER"
    ensure_writable_dirs
}

# ── main ─────────────────────────────────────────────────────────────────────

main() {
    info "tennisintehran deploy — $(date)"
    info "App dir: ${ROOT}"

    ensure_php
    find_composer
    ensure_git
    ensure_writable_dirs

    git_pull
    verify_build_assets
    ensure_db_config
    ensure_app_key

    local first_run=false
    if [[ ! -f "$MARKER" ]]; then
        first_run=true
    fi

    composer_install
    ensure_app_key

    artisan_migrate
    artisan_storage_link

    if $first_run; then
        maybe_seed
    fi

    clear_and_cache
    queue_restart

    mark_initialized

    info "Migration status (last 20):"
    $PHP_BIN artisan migrate:status | tail -n 20 || true

    info "Deploy completed: $(git rev-parse --short HEAD) at $(date)"
    info "Site: $(env_get APP_URL 2>/dev/null || echo 'check APP_URL in .env')"
    info "Admin: /admin"
}

main "$@"
