#!/bin/bash
# dbwrite — Hardened mesh DB writer with parameterized input
set -euo pipefail
shopt -s inherit_errexit 2>/dev/null || true
IFS=$'\n\t'

readonly NODE_ID="${NODE_ID:-669427}"
readonly VAULT_KEY="${VAULT_KEY:-/dev/shm/vault.key}"
readonly CRED_FILE="/run/se/.dbcred"
readonly LOG_FILE="/var/log/s3rv1c3agent/heartbeat.log"
readonly LOCK_FILE="/run/se/.dbwrite.lock"
readonly MAX_SQL_LEN=4096

# ── SQL Sanitizer ──────────────────────────────────────────────────────────
sql_escape() {
    local input="${1:-}"
    # Remove null bytes and control chars, escape single quotes
    printf '%s' "${input}" | tr -d '\0\x01-\x08\x0b\x0c\x0e-\x1f' | sed "s/'/''/g"
}

# ── Load credentials securely ──────────────────────────────────────────────
load_creds() {
    if [[ -f "${CRED_FILE}" ]]; then
        # Validate cred file permissions
        local perms
        perms="$(stat -c '%a' "${CRED_FILE}" 2>/dev/null || printf '%s' '000')"
        if [[ "${perms}" != "600" ]]; then
            printf 'ERR: cred file has insecure permissions: %s\n' "${perms}" >&2
            exit 1
        fi
        source "${CRED_FILE}"
        return 0
    fi

    # Derive from vault — NO hardcoded passwords
    local vk
    if ! vk="$(cat "${VAULT_KEY}" 2>/dev/null)" || [[ -z "${vk}" ]]; then
        printf 'ERR: no vault key available\n' >&2
        exit 1
    fi

    # In production: decrypt from vault
    # For now, expect environment variables
    DB_HOST="${DB_HOST:-}"
    DB_PORT="${DB_PORT:-3306}"
    DB_USER="${DB_USER:-}"
    DB_PASS="${DB_PASS:-}"
    DB_NAME="${DB_NAME:-store}"

    if [[ -z "${DB_HOST}" || -z "${DB_USER}" || -z "${DB_PASS}" ]]; then
        printf 'ERR: DB credentials not configured. Set DB_HOST, DB_USER, DB_PASS\n' >&2
        exit 1
    fi

    # Cache in tmpfs with strict permissions
    mkdir -p "$(dirname "${CRED_FILE}")"
    {
        printf 'DB_HOST="%s"\n' "${DB_HOST}"
        printf 'DB_PORT="%s"\n' "${DB_PORT}"
        printf 'DB_USER="%s"\n' "${DB_USER}"
        printf 'DB_PASS="%s"\n' "${DB_PASS}"
        printf 'DB_NAME="%s"\n' "${DB_NAME}"
    } > "${CRED_FILE}"
    chmod 600 "${CRED_FILE}"
}

# ── DB execution with timeout ──────────────────────────────────────────────
db_exec() {
    local sql="${1:-}"
    if [[ "${#sql}" -gt "${MAX_SQL_LEN}" ]]; then
        printf 'ERR: SQL exceeds max length\n' >&2
        return 1
    fi
    timeout 15 mysql -h"${DB_HOST}" -P"${DB_PORT}" -u"${DB_USER}" -p"${DB_PASS}" "${DB_NAME}" -N -e "${sql}" 2>/dev/null
}

# ── File lock ──────────────────────────────────────────────────────────────
acquire_lock() {
    exec 300>"${LOCK_FILE}"
    if ! flock -n 300; then
        printf 'ERR: dbwrite already running\n' >&2
        exit 1
    fi
}

# ── Main ───────────────────────────────────────────────────────────────────
load_creds

ACTION="${1:-heartbeat}"
shift || true

# Validate action
readonly VALID_ACTIONS="heartbeat tool_run event integrity check_integrity"
if [[ ! " ${VALID_ACTIONS} " =~ " ${ACTION} " ]]; then
    printf 'ERR: unknown action: %s\n' "${ACTION}" >&2
    exit 1
fi

case "${ACTION}" in
    heartbeat)
        db_exec "INSERT INTO sec_tool_runs (node_id, tool_name, command, exit_code, started_at, duration_ms, status_code) VALUES ('$(sql_escape "${NODE_ID}")', 'heartbeat', 'hb_send', 0, NOW(), 0, 5001);"
        printf '%s heartbeat OK\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" >> "${LOG_FILE}" 2>/dev/null || true
        ;;

    tool_run)
        TOOL="${1:-unknown}"
        EXIT="${2:-0}"
        DUR="${3:-0}"
        CMD="${4:-run}"
        # Validate numeric inputs
        [[ "${EXIT}" =~ ^[0-9]+$ ]] || EXIT=0
        [[ "${DUR}" =~ ^[0-9]+$ ]] || DUR=0
        db_exec "INSERT INTO sec_tool_runs (node_id, tool_name, command, exit_code, started_at, duration_ms, status_code) VALUES ('$(sql_escape "${NODE_ID}")', '$(sql_escape "${TOOL}")', '$(sql_escape "${CMD}")', ${EXIT}, NOW(), ${DUR}, 5001);"
        ;;

    event)
        ETYPE="${1:-info}"
        SEVERITY="${2:-info}"
        DETAIL="${3:-}"
        SOURCE="${4:-s3rv1c3agent}"
        db_exec "INSERT INTO sec_events (event_uuid, node_id, source_tag, event_type, severity, payload_enc, created_at) VALUES (UUID(), '$(sql_escape "${NODE_ID}")', '$(sql_escape "${SOURCE}")', '$(sql_escape "${ETYPE}")', '$(sql_escape "${SEVERITY}")', '$(sql_escape "${DETAIL}")', NOW());"
        ;;

    integrity)
        FILE="${1:-}"
        EXPECTED="${2:-}"
        ACTUAL="${3:-}"
        MATCHED=$([[ "${EXPECTED}" == "${ACTUAL}" ]] && printf '%s' "1" || printf '%s' "0")
        db_exec "INSERT INTO sec_bash_integrity (node_id, file_path, expected_hash, actual_hash, matched, checked_at, status_code) VALUES ('$(sql_escape "${NODE_ID}")', '$(sql_escape "${FILE}")', '$(sql_escape "${EXPECTED}")', '$(sql_escape "${ACTUAL}")', ${MATCHED}, NOW(), 5001);"
        ;;

    check_integrity)
        BASELINE="/run/se/state/baselines.sha256"
        if [[ ! -f "${BASELINE}" ]]; then
            printf 'NO_BASELINE\n' >&2
            exit 1
        fi
        local PASS=0 FAIL=0
        local hash file actual matched
        while IFS='  ' read -r hash file; do
            [[ -n "${hash}" && -n "${file}" ]] || continue
            actual="$(sha256sum "${file}" 2>/dev/null | awk '{print $1}')" || actual="MISSING"
            matched=$([[ "${hash}" == "${actual}" ]] && printf '%s' "1" || printf '%s' "0")
            db_exec "INSERT INTO sec_bash_integrity (node_id, file_path, expected_hash, actual_hash, matched, checked_at, status_code) VALUES ('$(sql_escape "${NODE_ID}")', '$(sql_escape "${file}")', '$(sql_escape "${hash}")', '$(sql_escape "${actual}")', ${matched}, NOW(), 5001);"
            if [[ "${matched}" -eq 1 ]]; then
                ((PASS++)) || true
            else
                ((FAIL++)) || true
            fi
        done < "${BASELINE}"
        printf 'integrity: %d pass, %d fail\n' "${PASS}" "${FAIL}"
        ;;
esac
