#!/bin/bash
# ==============================================================================================
# ============================= Emby Database Repair ===========================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Stops Emby, runs SQLite PRAGMA integrity_check on all Emby databases, and
# restarts. Use when Emby reports corruption, unexpected crashes, or playback
# state issues.
#
# Reports which databases are corrupted. Does NOT automatically repair.
# Repair requires manual steps — guidance is printed in the summary output.
# Always take a backup before deleting any database file.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
# Databases Checked
#   library.db        — media library metadata (largest, most critical)
#   library.db-wal    — write-ahead log (if present — uncommitted transactions)
#   librarydb.db      — legacy library database
#   users.db          — user accounts and settings
#   authentication.db — API keys and sessions
#   activity.db       — activity log (least critical, safe to delete if corrupt)
#
# Missing databases are skipped gracefully — not all files exist on all setups.
# Emby's config path is detected from the Docker mount — no hardcoded paths.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Diagnose, Never Repair
#   The script reports which databases fail integrity_check and stops there. SQLite
#   "repair" means dumping and rebuilding, which silently discards whatever rows were
#   corrupt — an outcome nobody should get without deciding to. The remediation steps
#   are printed instead, for a human to run against a backup.
#
# Stop Emby Before Reading
#   integrity_check against a live database gives unreliable answers and can itself
#   contend with Emby's writes. Emby is stopped for the duration and restarted after,
#   so the check runs against a quiescent file.
#
# Always Restart, Even on Failure
#   Emby is brought back up regardless of what the check found, and an EXIT trap armed
#   before it is stopped restores it even if this script dies partway through. It is
#   disarmed only once the normal restart has run. Leaving the media server down because
#   a diagnostic reported a problem turns an investigation into an outage — and the
#   original running state is honoured, so an Emby that was already stopped stays stopped.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Guaranteed Restart
#   EXIT trap ensures Emby is always restarted even if the script crashes
#   mid-check — Emby is never left stopped due to a script error.
#
# Docker Timeout
#   DOCKER_TIMEOUT (30s) protects all docker calls against a hung daemon.
#   Emby can take time to stop cleanly — 30s is intentionally generous.
#
# Tool Validation
#   platform_require_cmd confirms sqlite3 and the notify script are present
#   before use. jq is checked separately — required for config path detection.
#
# Post-Restart Verify
#   Checks that Emby is still running after restart — detects cases where
#   Emby crashes immediately after start (which would indicate deeper trouble).
#
# Silent When Healthy
#   Only corruption produces visible output and a notification.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf
#
#   HOST*_EMBY_CONTAINER
#       Name of the Emby Docker container on this host.
#       Aliased by detect_hosts() → EMBY_CONTAINER.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# emby_database_repair.sh
#     Stop Emby, check all databases with PRAGMA integrity_check, restart.
#
# emby_database_repair.sh --dry-run
#     Show which databases would be checked and Emby container name. No stop.
#
# emby_database_repair.sh --log
#     Verbose output with per-database check result.
#
# emby_database_repair.sh --status
#     Show Emby container name and config path detected from Docker. Then exit.
#
# ==============================================================================================

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

source "$SCRIPT_DIR/../load_config.sh"

parse_args "$@"

DOCKER_TIMEOUT=30   # Emby can take time to stop cleanly

# ==============================================================================================
# ━━━ Setup ━━━
# ==============================================================================================
if [[ "$EUID" -ne 0 ]]; then
    error "Must be run as root"
    exit 1
fi

# Validate required tools
platform_require_cmd \
    "$(command -v sqlite3 2>/dev/null || echo /usr/bin/sqlite3)" \
    "--version" "." \
    "sqlite3" || { error "sqlite3 not found — install sqlite package"; exit 1; }


if ! command -v jq >/dev/null 2>&1; then
    error "jq not found — required to detect Emby config path from Docker mounts"
    exit 1
fi

acquire_lock

if ! command -v docker &>/dev/null; then
    error "Docker command not found"
    exit 1
fi

# detect_hosts() sets MY_ID and aliases HOST*_EMBY_CONTAINER → EMBY_CONTAINER
detect_hosts

if [[ -z "${EMBY_CONTAINER:-}" ]]; then
    error "EMBY_CONTAINER not set for $MY_ID — check HOST*_EMBY_CONTAINER in host*.conf"
    exit 1
fi

log "Identity:       $MY_ID ($LOCAL_SERVER_NAME)"
log "Emby container: $EMBY_CONTAINER"

# Detect Emby config path from Docker container mounts
EMBY_CONFIG_HOST=$(timeout "$DOCKER_TIMEOUT" docker inspect "$EMBY_CONTAINER" 2>/dev/null | \
    jq -r '.[] | .Mounts[] | select(.Destination == "/config") | .Source' 2>/dev/null)

if [[ -z "$EMBY_CONFIG_HOST" ]]; then
    error "Could not detect Emby config path from Docker mounts"
    error "Is $EMBY_CONTAINER the correct container name? Check HOST*_EMBY_CONTAINER in host*.conf"
    exit 1
fi

log "Emby config: $EMBY_CONFIG_HOST"

[[ "$DRY_RUN" == true ]] && warn "DRY RUN — Emby will not be stopped, no checks run"

# ==============================================================================================
# ━━━ Status ━━━
# ==============================================================================================
if [[ "$SHOW_STATUS" == true ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
    echo "$ICON_HOST    Identity:   $MY_ID ($LOCAL_SERVER_NAME)"
    echo "$ICON_EMBY    Container:  $EMBY_CONTAINER"
    echo "$ICON_EMBY    Config:     $EMBY_CONFIG_HOST"
    echo "$ICON_GEAR    Dry Run:    $DRY_RUN"
    echo ""
    echo "━━━ Database Files ━━━"
    for db_rel in "data/library.db" "data/library.db-wal" "data/librarydb.db" \
                  "data/users.db" "data/authentication.db" "data/activity.db"; do
        db_path="${EMBY_CONFIG_HOST}/${db_rel}"
        db_name=$(basename "$db_rel")
        if [[ -f "$db_path" ]]; then
            db_size=$(du -sh "$db_path" 2>/dev/null | cut -f1)
            echo "  $ICON_SUCCESS $db_name ($db_size)"
        else
            echo "  $ICON_SKIP $db_name — not found"
        fi
    done
    echo "━━━━━━━━━━━━━━━━━━━━━━━"
    exit 0
fi

# ── EXIT trap — Emby always restarted if it was running ───────────────────────────────────────
EMBY_WAS_RUNNING=false

cleanup_on_exit() {
    local exit_code=$?
    container_force_restart_if_needed "$EMBY_CONTAINER" "$EMBY_WAS_RUNNING"
}

trap cleanup_on_exit EXIT

# ==============================================================================================
# ━━━ Stop Emby ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_STOP Stop Emby ━━━"

container_stop_for_maintenance "$EMBY_CONTAINER" EMBY_WAS_RUNNING \
    "Stopping $EMBY_CONTAINER — active sessions will be interrupted" warn 3 || exit 1
[[ "$EMBY_WAS_RUNNING" == false ]] && \
    log "$EMBY_CONTAINER is not running — proceeding with checks"

# ==============================================================================================
# ━━━ Database Integrity Check ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_HEALTH Database Integrity Check ━━━"

START=$(date +%s)

DB_FILES=(
    "data/library.db"
    "data/library.db-wal"
    "data/librarydb.db"
    "data/users.db"
    "data/authentication.db"
    "data/activity.db"
)

PASS_DBS=()
FAIL_DBS=()
MISSING_DBS=()

for db_rel in "${DB_FILES[@]}"; do
    db_path="${EMBY_CONFIG_HOST}/${db_rel}"
    db_name=$(basename "$db_rel")

    if [[ ! -f "$db_path" ]]; then
        log "$db_name — not found, skipping"
        MISSING_DBS+=("$db_name")
        continue
    fi

    DB_SIZE=$(du -sh "$db_path" 2>/dev/null | cut -f1)
    log "Checking $db_name ($DB_SIZE)..."

    # WAL file — different check (not a full SQLite database)
    if [[ "$db_name" == *.wal ]]; then
        if [[ -s "$db_path" ]]; then
            warn "$db_name exists and is non-empty (${DB_SIZE})"
            warn "Uncommitted WAL data — will be merged when Emby next starts cleanly"
            PASS_DBS+=("$db_name (WAL — see warning)")
        else
            echo "$db_name exists but is empty — no pending transactions ✅"
            PASS_DBS+=("$db_name")
        fi
        continue
    fi

    if [[ "$DRY_RUN" == true ]]; then
        warn "DRY RUN — would check: $db_path"
        continue
    fi

    # Full integrity check
    RESULT=$(sqlite3 "$db_path" "PRAGMA integrity_check;" 2>/dev/null)
    EXIT_CODE=$?

    if [[ "$EXIT_CODE" -ne 0 ]]; then
        error "$db_name — sqlite3 could not open database (locked or corrupt)"
        FAIL_DBS+=("$db_name")
    elif [[ "$RESULT" == "ok" ]]; then
        echo "$db_name — integrity check passed ✅"
        PASS_DBS+=("$db_name")
    else
        error "$db_name — CORRUPTION DETECTED"
        echo "$RESULT" | head -10 | while IFS= read -r line; do
            error "  $line"
        done
        FAIL_DBS+=("$db_name")
    fi
done

END=$(date +%s)

# ==============================================================================================
# ━━━ Restart Emby ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_START Restart Emby ━━━"

container_restart_after_maintenance "$EMBY_CONTAINER" "$EMBY_WAS_RUNNING" 5 "Emby DB Repair"
[[ "$RESTART_OK" == false ]] && error "Check Docker logs: docker logs $EMBY_CONTAINER"

# Clear EXIT trap — clean exit
trap - EXIT

# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY EMBY DATABASE REPAIR SUMMARY ━━━━━"
echo "$ICON_HOST    Identity:   $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_EMBY    Container:  $EMBY_CONTAINER"
echo "$ICON_EMBY    Config:     $EMBY_CONFIG_HOST"
echo "$ICON_TIME    Duration:   $(format_duration $(( END - START )))"
echo ""
echo "  $ICON_SUCCESS Passed:  ${#PASS_DBS[@]}"
[[ ${#FAIL_DBS[@]} -gt 0 ]]    && echo "  $ICON_ERROR  Failed:  ${#FAIL_DBS[@]}"
[[ ${#MISSING_DBS[@]} -gt 0 ]] && echo "  Skipped: ${#MISSING_DBS[@]} (not found)"
echo ""

[[ ${#PASS_DBS[@]} -gt 0 ]] && for db in "${PASS_DBS[@]}"; do echo "  $ICON_SUCCESS $db"; done
[[ ${#FAIL_DBS[@]} -gt 0 ]] && for db in "${FAIL_DBS[@]}"; do echo "  $ICON_ERROR  $db"; done

echo ""

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN — no checks performed"
elif [[ ${#FAIL_DBS[@]} -gt 0 ]]; then
    echo "$ICON_ERROR Status: CORRUPTION FOUND — manual intervention needed"
    echo ""
    echo "$ICON_INFO Next steps per corrupted database:"
    echo "  library.db       — Delete file, restart Emby — rebuilds from media (slow first start)"
    echo "  library.db-wal   — Delete WAL file, restart Emby — safe, no permanent data loss"
    echo "  librarydb.db     — Delete file, restart Emby — legacy, Emby recreates"
    echo "  users.db         — Restore from backup or delete — deleting resets all user accounts"
    echo "  authentication.db — Delete file, restart Emby — API keys regenerated automatically"
    echo "  activity.db      — Delete file, restart Emby — activity log only, no media data"
    echo ""
    warn "⚠️  Always take a backup before deleting any database file"
    warn "   Run: container_data_export.sh $EMBY_CONTAINER <config_path> <backup_dir>"
    notify "Emby database CORRUPTION on $(hostname) — failed: ${FAIL_DBS[*]} — manual intervention needed" \
        "Emby DB Repair" "warning"
else
    echo "$ICON_DONE Status: all ${#PASS_DBS[@]} databases healthy ✅"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

[[ ${#FAIL_DBS[@]} -gt 0 ]] && exit 1
exit 0