#!/usr/bin/env bash
# =============================================================================
# onx-file-copy — Copy file or directory under /home/<user>
#
# Purpose:
#   `cp -a` equivalent with strict path validation. Both source and destination
#   must resolve under /home/<username>/. Refuses to overwrite unless force=true.
#
# Input (stdin JSON):
#   {
#     "username":   "onx_xxxx",
#     "src":        "public_html/index.html",   -- required; rel to home
#     "dst":        "public_html/index.bak",    -- required; rel to home
#     "force":      false,                      -- optional; default false
#     "preserve":   true                        -- optional; default true (cp -a)
#   }
#
# Output (stdout JSON):
#   {
#     "username":    "onx_xxxx",
#     "src":         "/home/onx_xxxx/public_html/index.html",
#     "dst":         "/home/onx_xxxx/public_html/index.bak",
#     "is_dir":      false,
#     "bytes":       1247,
#     "files_count": 1,
#     "overwritten": false
#   }
#
# Exit codes: 0=ok 1=invalid-input 2=preflight-fail 3=execution-fail
# Deployed to: /usr/local/onoxsoft/bin/onx-file-copy
# =============================================================================

set -euo pipefail

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

require_cmd jq
require_cmd cp
require_cmd find
require_cmd stat

onx_json_input

USERNAME="$(onx_json_field username)"
SRC_REL="$(onx_json_field src)"
DST_REL="$(onx_json_field dst)"
FORCE="$(onx_json_get_bool "$INPUT" force false)"
PRESERVE="$(onx_json_get_bool "$INPUT" preserve true)"

onx_validate_username "$USERNAME"
[[ -z "${SRC_REL}" ]] && onx_die 1 "src is required"
[[ -z "${DST_REL}" ]] && onx_die 1 "dst is required"

HOME_DIR="/home/${USERNAME}"
[[ -d "$HOME_DIR" ]] || onx_die 2 "home directory missing: ${HOME_DIR}"

# Resolve src (must exist) and dst (may not exist yet).
SRC_ABS="$(realpath -e "${HOME_DIR}/${SRC_REL#/}" 2>/dev/null)" \
    || onx_die 1 "source not found: ${SRC_REL}"

DST_INPUT="${HOME_DIR}/${DST_REL#/}"
DST_ABS="$(realpath -m "${DST_INPUT}" 2>/dev/null || printf '%s' "${DST_INPUT}")"

# Path-traversal guard — both must remain under HOME_DIR.
case "${SRC_ABS}" in
    "${HOME_DIR}"|"${HOME_DIR}"/*) ;;
    *) onx_die 1 "src escapes /home/${USERNAME}: ${SRC_REL}" ;;
esac
case "${DST_ABS}" in
    "${HOME_DIR}"|"${HOME_DIR}"/*) ;;
    *) onx_die 1 "dst escapes /home/${USERNAME}: ${DST_REL}" ;;
esac

# Refuse copying onto itself.
[[ "${SRC_ABS}" == "${DST_ABS}" ]] && onx_die 1 "src and dst are identical"

# Overwrite handling.
OVERWRITTEN="false"
if [[ -e "${DST_ABS}" ]]; then
    if [[ "${FORCE}" != "true" ]]; then
        onx_die 2 "destination exists (use force=true to overwrite): ${DST_REL}"
    fi
    OVERWRITTEN="true"
fi

# Reject copying into one of src's own subdirectories.
if [[ -d "${SRC_ABS}" ]]; then
    case "${DST_ABS}/" in
        "${SRC_ABS}/"*) onx_die 1 "dst is inside src — cannot recurse into itself" ;;
    esac
fi

# Ensure parent dir of dst exists.
DST_PARENT="$(dirname "${DST_ABS}")"
[[ -d "${DST_PARENT}" ]] || mkdir -p "${DST_PARENT}"

# ── Execute copy ────────────────────────────────────────────────────────────
CP_FLAGS=()
[[ "${PRESERVE}" == "true" ]] && CP_FLAGS+=(-a) || CP_FLAGS+=(-r)
# --no-dereference defaults under -a; without -a we add it explicitly.
[[ "${PRESERVE}" == "true" ]] || CP_FLAGS+=(--no-dereference)
# Overwrite existing without prompting (we already guarded via $FORCE).
CP_FLAGS+=(-f)

cp "${CP_FLAGS[@]}" -- "${SRC_ABS}" "${DST_ABS}" 2>/dev/null \
    || onx_die 3 "cp failed: ${SRC_REL} → ${DST_REL}"

# ── Stats ───────────────────────────────────────────────────────────────────
IS_DIR="false"
[[ -d "${DST_ABS}" ]] && IS_DIR="true"

BYTES=0
FILES_COUNT=1
if [[ "${IS_DIR}" == "true" ]]; then
    BYTES="$(du -sb "${DST_ABS}" 2>/dev/null | awk '{print $1}')"
    FILES_COUNT="$(find "${DST_ABS}" -type f 2>/dev/null | wc -l)"
else
    BYTES="$(stat -c '%s' "${DST_ABS}" 2>/dev/null || echo 0)"
fi
BYTES="${BYTES:-0}"
FILES_COUNT="${FILES_COUNT:-1}"

onx_log "file-copy: user=${USERNAME} src='${SRC_REL}' dst='${DST_REL}' bytes=${BYTES} files=${FILES_COUNT} overwritten=${OVERWRITTEN}"

jq -nc \
    --arg username "${USERNAME}" \
    --arg src "${SRC_ABS}" \
    --arg dst "${DST_ABS}" \
    --argjson is_dir "$([[ "${IS_DIR}" == "true" ]] && echo true || echo false)" \
    --argjson bytes "${BYTES}" \
    --argjson files_count "${FILES_COUNT}" \
    --argjson overwritten "$([[ "${OVERWRITTEN}" == "true" ]] && echo true || echo false)" \
    '{
        username: $username,
        src: $src,
        dst: $dst,
        is_dir: $is_dir,
        bytes: $bytes,
        files_count: $files_count,
        overwritten: $overwritten
     }'
