#!/usr/bin/env bash
# =============================================================================
# onx-user-add — Create Linux hosting account with cPanel-grade skeleton + XFS quota
#
# Purpose:
#   Provisions a new Linux user account for an ONOX hosting customer.
#   Lays down a 15-directory cPanel-grade home skeleton (public_html, mail,
#   logs, ssl, .onoxsoft, .htpasswds, ...), drops a default index.html /
#   .htaccess into public_html, writes bash dotfiles, applies SELinux
#   contexts when available, and applies an XFS quota from the package.
#
# Input (stdin JSON):
#   {
#     "username":   "onx_xxxx",          -- required; ^onx_[a-z0-9]{4,12}$
#     "email":      "user@example.com",  -- required; stored in .onoxsoft/meta.json
#     "package":    "starter",           -- optional; used to select quota limits
#     "domain":     "example.com",       -- optional; used for index.html title
#     "uid":        null,                -- optional; null = auto-assign
#     "quota_bytes": 2147483648,         -- optional; overrides package map
#     "home_base":  "/home",             -- optional; default /home
#     "uid_min":    10000,               -- reserved for future range checks
#     "uid_max":    65000                -- reserved for future range checks
#   }
#
# Output (stdout JSON):
#   {
#     "username":..., "uid":..., "gid":..., "home":...,
#     "skeleton": "cpanel-grade", "dirs_created": 15,
#     "default_index": true, "default_htaccess": true,
#     "created_at": "<ISO8601>"
#   }
#
# Exit codes:
#   0 ok / 1 invalid input / 2 preflight / 3 exec / 4 exec+rollback ok /
#   5 exec+rollback FAILED (critical)
#
# Sudoers entry needed:
#   apache ALL=(root) NOPASSWD: /usr/local/onoxsoft/bin/onx-user-add
#   Defaults!/usr/local/onoxsoft/bin/onx-user-add !requiretty
#   Defaults!/usr/local/onoxsoft/bin/onx-user-add log_output, log_input
#
# Deployed to: /usr/local/onoxsoft/bin/onx-user-add
# =============================================================================

set -euo pipefail

SCRIPT_DIR="$(dirname "$(readlink -f "$0")")"
# shellcheck source=_lib/common.sh
source "${SCRIPT_DIR}/_lib/common.sh"

# ── Constants ─────────────────────────────────────────────────────────────────
ONX_SKEL="/etc/skel.onoxsoft"   # ONOXSOFT'a özel — Linux /etc/skel'den ayrı
ONX_GROUP="onoxsoft-users"
APACHE_GROUP="apache"
VMAIL_USER="vmail"
HOME_BASE_DEFAULT="/home"
META_DIR=".onoxsoft"

# Package → disk quota (MB) mapping
declare -A PKG_DISK_MB=(
    ["starter"]=2048
    ["basic"]=5120
    ["pro"]=20480
    ["business"]=51200
    ["enterprise"]=102400
)
declare -A PKG_INODES=(
    ["starter"]=100000
    ["basic"]=250000
    ["pro"]=1000000
    ["business"]=2500000
    ["enterprise"]=5000000
)

# ── Dependencies ──────────────────────────────────────────────────────────────
command -v jq       >/dev/null 2>&1 || { printf '{"error":"jq required"}\n' >&2; exit 2; }
command -v useradd  >/dev/null 2>&1 || { printf '{"error":"useradd required"}\n' >&2; exit 2; }
command -v setquota >/dev/null 2>&1 || { printf '{"error":"setquota required"}\n' >&2; exit 2; }
require_root

# ── Read & parse stdin ────────────────────────────────────────────────────────
INPUT=$(cat)
onx_require_json "${INPUT}"

USERNAME=$(onx_json_get   "${INPUT}" "username")
EMAIL=$(onx_json_get      "${INPUT}" "email")
PACKAGE=$(onx_json_get    "${INPUT}" "package" "starter")
DOMAIN=$(onx_json_get     "${INPUT}" "domain" "")
UID_HINT=$(onx_json_get   "${INPUT}" "uid" "")
QUOTA_BYTES=$(onx_json_get "${INPUT}" "quota_bytes" "")
HOME_BASE=$(onx_json_get  "${INPUT}" "home_base" "${HOME_BASE_DEFAULT}")

# ── Input validation ──────────────────────────────────────────────────────────
onx_validate_username "${USERNAME}"

# Runtime detect home directory (handles /home/users/<user> vs /home/<user>)
USER_HOME=$(onx_resolve_home "${USERNAME}" 2>/dev/null || echo "/home/${USERNAME}")

[[ -z "${EMAIL}" ]] && onx_die 1 "email is required"
[[ "${EMAIL}" =~ ^[^@]+@[^@]+\.[^@]+$ ]] || onx_die 1 "invalid email: ${EMAIL}"
[[ -n "${DOMAIN}" ]] && onx_validate_domain "${DOMAIN}"

# ── Preflight ─────────────────────────────────────────────────────────────────
# IDEMPOTENT: user zaten varsa (önceki başarısız etkinleştirmeden orphan) skip
# edip provisioning'in geri kalanını uygula. Bu sayede panel "etkinleştir"
# butonu birden çok kez basılırsa idempotent çalışır.
USER_EXISTS=0
if id "${USERNAME}" &>/dev/null; then
    USER_EXISTS=1
    onx_log "user already exists, skipping useradd (idempotent re-activation)"
fi
getent group "${ONX_GROUP}" &>/dev/null || onx_die 2 "group not found: ${ONX_GROUP}"

# ── Build useradd args ────────────────────────────────────────────────────────
UID_ARGS=()
if [[ -n "${UID_HINT}" && "${UID_HINT}" != "null" ]]; then
    UID_ARGS=("-u" "${UID_HINT}")
fi

SKEL_ARGS=()
if [[ -d "${ONX_SKEL}" ]]; then
    SKEL_ARGS=("-k" "${ONX_SKEL}")
fi

trap 'onx_rollback_run' ERR

# ── Create user (sadece yoksa) ──────────────────────────────────────────────
if [[ "${USER_EXISTS}" -eq 0 ]]; then
    useradd -m \
        -s /bin/bash \
        -d "${HOME_BASE}/${USERNAME}" \
        -g "${ONX_GROUP}" \
        "${SKEL_ARGS[@]}" \
        "${UID_ARGS[@]}" \
        "${USERNAME}"

    onx_rollback_register "userdel -r '${USERNAME}' 2>/dev/null || true"
    onx_log "useradd OK: ${USERNAME}"
else
    # User var ama home eksikse oluştur (corrupt state recovery)
    if [[ ! -d "${HOME_BASE}/${USERNAME}" ]]; then
        mkdir -p "${HOME_BASE}/${USERNAME}"
        chown "${USERNAME}:${ONX_GROUP}" "${HOME_BASE}/${USERNAME}"
        # Skel dosyaları kopyala
        if [[ -d "${ONX_SKEL}" ]]; then
            cp -rT "${ONX_SKEL}" "${HOME_BASE}/${USERNAME}"
            chown -R "${USERNAME}:${ONX_GROUP}" "${HOME_BASE}/${USERNAME}"
        fi
        onx_log "home dir recreated for existing user: ${HOME_BASE}/${USERNAME}"
    fi
fi

HOME_DIR="${HOME_BASE}/${USERNAME}"
chmod 0711 "${HOME_DIR}"   # other=x so Apache can traverse, no read

# Backward compat symlink — eski hardcoded ${USER_HOME} referansları
# için. /home/onx_user → /home/users/onx_user. Eski scripts'lerin (henüz
# onx_resolve_home migration olmamış olanların) çalışmaya devam etmesi için.
# Eğer HOME_BASE zaten /home ise symlink gereksiz; sadece /home/users gibi
# subfolder durumunda yarat.
if [[ "${HOME_BASE}" != "/home" && ! -e "${USER_HOME}" ]]; then
    ln -sf "${HOME_DIR}" "${USER_HOME}"
    onx_log "compat symlink: ${USER_HOME} -> ${HOME_DIR}"
fi

# v3.51: User'ı HEM apache HEM webserver group'una ekle (multi-driver pattern).
# webserver group: apache+nginx+caddy+lsws hepsinin üye olduğu shared group.
# Bu olmadan OLS (nobody runtime) docroot'a erişemez → 403/404.
if getent group "${APACHE_GROUP}" &>/dev/null; then
    usermod -a -G "${APACHE_GROUP}" "${USERNAME}" || true
fi
if getent group "webserver" &>/dev/null; then
    usermod -a -G "webserver" "${USERNAME}" || true
fi

# ── cPanel-grade skeleton (15 system dirs) ────────────────────────────────────
# Notes:
#   - skip .cpanel: ONOXSOFT uses .onoxsoft instead
#   - skip .koality / .razor / .spamassassin: we use Rspamd
#   - .trash is the soft-delete bin (File Manager "Trash")
#   - lscache only created if LiteSpeed is present
declare -A SKEL_DIRS=(
    ["public_html"]="0750"          # DocumentRoot — Apache reads via apache grp
    ["public_ftp"]="0755"           # Anonymous FTP public area
    ["mail"]="0700"                 # Maildir parent (chowned to vmail later)
    ["tmp"]="1777"                  # sticky — PHP upload_tmp_dir, sessions
    ["logs"]="0750"                 # Apache per-domain access/error logs
    ["ssl"]="0700"                  # SSL cert symlinks
    ["etc"]="0750"                  # per-user readonly config
    [".trash"]="0700"               # File Manager soft-delete bin
    [".htpasswds"]="0700"           # directory_privacy htpasswd files
    [".wp-cli"]="0700"              # wp-cli per-user config
    [".subaccounts"]="0700"         # sub-account info
    [".caldav"]="0700"              # CalDAV calendar data
    ["wordpress-backups"]="0750"    # WP Toolkit backup destination
    [".onoxsoft"]="0700"            # Panel per-user state (meta.json etc.)
    ["perl5"]="0750"                # Per-user Perl module install path
)

DIRS_CREATED=0
for dir in "${!SKEL_DIRS[@]}"; do
    mkdir -p "${HOME_DIR}/${dir}"
    chmod "${SKEL_DIRS[$dir]}" "${HOME_DIR}/${dir}"
    DIRS_CREATED=$((DIRS_CREATED + 1))
done

# ── Ownership: account user owns everything except mail/ ──────────────────────
chown -R "${USERNAME}:${ONX_GROUP}" "${HOME_DIR}"

# mail/ goes to vmail:vmail (Faz A — onx-mailbox-create chowns Maildir/ later)
if getent passwd "${VMAIL_USER}" &>/dev/null; then
    chown "${VMAIL_USER}:${VMAIL_USER}" "${HOME_DIR}/mail"
fi

# v3.51: public_html group = webserver (multi-driver: apache+nginx+ols+caddy)
# Eskiden APACHE_GROUP (apache veya onoxsoft-users) idi — OLS nobody erişemiyor → 403/404.
# webserver group: tüm web server runtime user'larının üye olduğu shared group.
# Fallback: webserver yoksa APACHE_GROUP (eski davranış korunur).
WEB_GROUP="webserver"
if ! getent group "${WEB_GROUP}" &>/dev/null; then
    WEB_GROUP="${APACHE_GROUP}"
fi
chgrp "${WEB_GROUP}" "${HOME_DIR}/public_html"
chmod g+rX "${HOME_DIR}/public_html"
onx_log "public_html group set to ${WEB_GROUP} (multi-driver compat)"

onx_log "skeleton created: ${DIRS_CREATED} dirs in ${HOME_DIR}"

# ── Default index.html (Coming Soon) ──────────────────────────────────────────
INDEX_TITLE="${DOMAIN:-Yeni Site}"
cat > "${HOME_DIR}/public_html/index.html" <<EOF
<!DOCTYPE html>
<html lang="tr">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>${INDEX_TITLE}</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
               background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
               min-height: 100vh; display: flex; align-items: center; justify-content: center;
               color: white; }
        .container { text-align: center; padding: 2rem; max-width: 600px; }
        h1 { font-size: 3rem; margin-bottom: 1rem; font-weight: 700; }
        p { font-size: 1.25rem; opacity: 0.9; margin-bottom: 0.5rem; }
        .badge { display: inline-block; margin-top: 2rem; padding: 0.5rem 1rem;
                 background: rgba(255,255,255,0.2); border-radius: 999px;
                 font-size: 0.875rem; backdrop-filter: blur(10px); }
    </style>
</head>
<body>
    <div class="container">
        <h1>Yakinda!</h1>
        <p>Sitenin yapim asamasinda oldugunu gormenize sevindik.</p>
        <p>Pek yakinda siz degerli ziyaretcilerimizi karsilayacagiz.</p>
        <div class="badge">Powered by ONOXSOFT Panel</div>
    </div>
</body>
</html>
EOF

# ── Default .htaccess (security + perf) ───────────────────────────────────────
cat > "${HOME_DIR}/public_html/.htaccess" <<EOF
# Onoxsoft default .htaccess — basic security + perf
# Customer ozel kurallari icin .htaccess.local kullanin
# (HtaccessProvisioner tarafindan yonetilir)

Options -Indexes
ServerSignature Off

# Block hidden files (.git, .env, vd.)
<FilesMatch "^\.">
    Require all denied
</FilesMatch>

# Block .ht* files (Apache default ama emin olalim)
<FilesMatch "^\.ht">
    Require all denied
</FilesMatch>

# Customer custom rules — varsa include et
<IfFile "${HOME_DIR}/public_html/.htaccess.local">
    Include ${HOME_DIR}/public_html/.htaccess.local
</IfFile>
EOF

chown "${USERNAME}:${ONX_GROUP}" \
    "${HOME_DIR}/public_html/index.html" \
    "${HOME_DIR}/public_html/.htaccess"
chmod 0644 \
    "${HOME_DIR}/public_html/index.html" \
    "${HOME_DIR}/public_html/.htaccess"

# ── Bash dotfiles ─────────────────────────────────────────────────────────────
cat > "${HOME_DIR}/.bash_profile" <<'EOF'
# .bash_profile
[ -f ~/.bashrc ] && . ~/.bashrc
PATH=$PATH:$HOME/bin
export PATH
EOF

cat > "${HOME_DIR}/.bashrc" <<'EOF'
# .bashrc
[ -z "$PS1" ] && return
[ -f /etc/bashrc ] && . /etc/bashrc
alias ll='ls -la'
alias l.='ls -d .* --color=auto'
PS1='[\u@\h \W]\$ '
EOF

cat > "${HOME_DIR}/.bash_logout" <<'EOF'
# .bash_logout
clear
EOF

chown "${USERNAME}:${ONX_GROUP}" \
    "${HOME_DIR}/.bash_profile" \
    "${HOME_DIR}/.bashrc" \
    "${HOME_DIR}/.bash_logout"
chmod 0644 \
    "${HOME_DIR}/.bash_profile" \
    "${HOME_DIR}/.bashrc" \
    "${HOME_DIR}/.bash_logout"

# ── Meta JSON (panel state) ───────────────────────────────────────────────────
CREATED_AT=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
cat > "${HOME_DIR}/${META_DIR}/meta.json" <<EOF
{
  "username": "${USERNAME}",
  "email": "${EMAIL}",
  "package": "${PACKAGE}",
  "primary_domain": "${DOMAIN}",
  "skeleton": "cpanel-grade",
  "skeleton_version": "1",
  "created_at": "${CREATED_AT}"
}
EOF
chown "${USERNAME}:${ONX_GROUP}" "${HOME_DIR}/${META_DIR}/meta.json"
chmod 0600 "${HOME_DIR}/${META_DIR}/meta.json"

# ── SELinux context (RHEL/AlmaLinux) ──────────────────────────────────────────
if command -v restorecon >/dev/null 2>&1; then
    if command -v semanage >/dev/null 2>&1; then
        semanage fcontext -a -t httpd_sys_content_t "${HOME_DIR}/public_html(/.*)?" 2>/dev/null || true
    fi
    restorecon -R "${HOME_DIR}/public_html" 2>/dev/null || true
    onx_log "SELinux contexts applied: ${HOME_DIR}/public_html"
fi

# ── Apply XFS quota ───────────────────────────────────────────────────────────
if [[ -n "${QUOTA_BYTES}" && "${QUOTA_BYTES}" != "null" && "${QUOTA_BYTES}" != "0" ]]; then
    DISK_MB=$(( QUOTA_BYTES / 1024 / 1024 ))
else
    DISK_MB="${PKG_DISK_MB[${PACKAGE}]:-${PKG_DISK_MB[starter]}}"
fi
INODES="${PKG_INODES[${PACKAGE}]:-${PKG_INODES[starter]}}"
SOFT_KB=$(( DISK_MB * 1024 ))
HARD_KB=$(( SOFT_KB * 110 / 100 ))   # 10% grace buffer

# Quota uygulanır SADECE filesystem'da quota aktifse.
#
# HOME_BASE (/home/users) bir dizin; setquota mount point bekler. Mount point'i
# findmnt ile tespit et — Contabo VPS'lerde /home/users genelde / (root)
# mount'unda yer alır. Quota check ve setquota gerçek mount point üzerinden.
QUOTA_MOUNT=$(findmnt -T "${HOME_BASE}" -no TARGET 2>/dev/null || echo "")
[[ -z "${QUOTA_MOUNT}" ]] && QUOTA_MOUNT="/"

QUOTA_ENABLED=0
if quotaon -p "${QUOTA_MOUNT}" 2>/dev/null | grep -qi "is on"; then
    QUOTA_ENABLED=1
elif xfs_quota -x -c "state" "${QUOTA_MOUNT}" 2>/dev/null | grep -qi "user quota state.*on"; then
    QUOTA_ENABLED=1
fi

if [[ "$QUOTA_ENABLED" -eq 1 ]]; then
    setquota -u "${USERNAME}" "${SOFT_KB}" "${HARD_KB}" "${INODES}" "$(( INODES * 110 / 100 ))" "${QUOTA_MOUNT}" || \
        onx_log "WARNING: setquota başarısız — disk limit atlandı (FS quota destekli değil)"
    onx_log "quota applied: ${DISK_MB}MB soft / $(( DISK_MB * 110 / 100 ))MB hard on ${QUOTA_MOUNT}"
else
    onx_log "WARNING: ${QUOTA_MOUNT} üzerinde quota aktif değil — disk limit uygulanmadı (UNLIMITED gibi davranır)"
fi

# ── v87: Deploy "provisioning" branded placeholder ────────────────────────────
# onx-default-page-deploy ile yenisi yazılır — env yoksa skip (sysapi başarısız
# olsa bile user-add success dönsün; cosmetic-only).
# inline heredoc index.html'i overwrite eder (templates dir varsa).
if [[ -x "${SCRIPT_DIR}/onx-default-page-deploy" ]]; then
    DEPLOY_PAYLOAD=$(jq -nc \
        --arg u "${USERNAME}" \
        --arg d "${DOMAIN:-unknown}" \
        --arg r "${HOME_DIR}/public_html" \
        --arg p "${PACKAGE}" \
        '{
            username: $u,
            domain: $d,
            state: "provisioning",
            docroot: $r,
            lang: "tr"
        }')
    echo "${DEPLOY_PAYLOAD}" | "${SCRIPT_DIR}/onx-default-page-deploy" >/dev/null 2>&1 || \
        onx_log "WARN: default-page-deploy provisioning skipped (template missing or sysapi error)"
fi

# ── Output ────────────────────────────────────────────────────────────────────
LINUX_UID=$(id -u "${USERNAME}")
LINUX_GID=$(id -g "${USERNAME}")

onx_json_out \
    "username"          "${USERNAME}" \
    "uid"               "${LINUX_UID}" \
    "gid"               "${LINUX_GID}" \
    "home"              "${HOME_DIR}" \
    "skeleton"          "cpanel-grade" \
    "dirs_created"      "${DIRS_CREATED}" \
    "default_index"     "true" \
    "default_htaccess"  "true" \
    "created_at"        "${CREATED_AT}"
