#!/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}"
[[ -z "${EMAIL}" ]] && onx_die 1 "email is required"
[[ "${EMAIL}" =~ ^[^@]+@[^@]+\.[^@]+$ ]] || onx_die 1 "invalid email: ${EMAIL}"
[[ -n "${DOMAIN}" ]] && onx_validate_domain "${DOMAIN}"

# ── Preflight ─────────────────────────────────────────────────────────────────
id "${USERNAME}" &>/dev/null && onx_die 2 "Linux user already exists: ${USERNAME}"
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 ───────────────────────────────────────────────────────────────
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}"

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

# Add user to apache group so Apache can read public_html (mode 0750)
if getent group "${APACHE_GROUP}" &>/dev/null; then
    usermod -a -G "${APACHE_GROUP}" "${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

# public_html group = apache so Apache can read with mode 0750
if getent group "${APACHE_GROUP}" &>/dev/null; then
    chgrp "${APACHE_GROUP}" "${HOME_DIR}/public_html"
fi

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

setquota -u "${USERNAME}" "${SOFT_KB}" "${HARD_KB}" "${INODES}" "$(( INODES * 110 / 100 ))" "${HOME_BASE}" || \
    onx_die 3 "setquota failed for ${USERNAME}"
onx_log "quota applied: ${DISK_MB}MB soft / $(( DISK_MB * 110 / 100 ))MB hard"

# ── 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}"
