#!/bin/bash
# ==============================================================================================
# ================================= Transcode Manager ==========================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Monitors ramdisk usage and manages the transcode symlink direction. Called by
# transcode_management.sh (Orchestrators/) every 7 minutes — always after
# transcode_cleanup.sh runs first. Must be fast, non-blocking, and silent when
# nothing has changed.
#
# Emby's transcode path points at TRANSCODE_LINK (a symlink). ffmpeg resolves
# the symlink once at session start and holds a direct reference — existing
# sessions are completely unaffected by symlink flips. Only new sessions care
# where the symlink currently points.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
# Three Modes (TRANSCODE_MANAGER_MODE)
#   smart   — auto-flips between ramdisk and SSD based on usage thresholds (default)
#             ramdisk above RAMDISK_WARN_GB → flip to SSD
#             ramdisk below RAMDISK_LOW_GB  → flip back to ramdisk
#   ramdisk — always uses ramdisk, warns if above threshold, never flips
#   ssd     — always uses SSD, never uses ramdisk (use during drain or maintenance)
#
# Safety Checks — Every Run Regardless of Mode
#   Symlink missing/broken    → recreate pointing at ramdisk, notify
#   Ramdisk disappeared       → flip to SSD immediately, notify warning
#   SSD path missing          → disable SSD fallback (error if mode=ssd)
#   transcoding-temp missing  → recreate on ramdisk silently
#   Permissions drift         → fix silently every run
#   Emby not running          → skip threshold checks, verify symlink only
#
# Session Display
#   Shows active streams from all configured TRANSCODE_SERVERS with user, title,
#   type (Live TV / TV Show / Movie), and play method (Transcode / Direct).
#   Split state shown when sessions exist on both ramdisk and SSD — normal during
#   a flip while ramdisk sessions drain.
#
# Daily Log
#   Appends one entry per run to TRANSCODE_DAILY_LOG, read by weekly_health_digest.sh.
#   Format: DATE|RAMDISK_USED_GB|FLIP_COUNT|RAM_SESSION_COUNT|SSD_SESSION_COUNT|FILES_CLEANED
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Flip the Destination, Never Move the Sessions
#   Switching targets only repoints the symlink so NEW sessions land elsewhere. Existing
#   transcodes keep writing to the path they opened and drain naturally. Moving files
#   mid-transcode would break every stream currently playing, which is the opposite of
#   what pressure relief is for.
#
# Asymmetric Thresholds
#   Flipping away happens at RAMDISK_WARN_GB, flipping back at RAMDISK_LOW_GB — two
#   separate values, not one. A single threshold would flip on every fluctuation around
#   it; the gap between them is what makes the decision stable under load.
#
# Safety Checks Are Unconditional
#   Symlink, ramdisk presence, SSD presence, transcoding-temp and permissions are all
#   verified on every run in every mode, including the fixed ramdisk/ssd modes. Mode
#   controls where transcodes go, not whether the plumbing gets checked.
#
# Degrade Toward the Fallback
#   Every failure path resolves toward SSD, never toward an unusable target. A vanished
#   ramdisk flips to SSD immediately rather than leaving sessions pointed at nothing —
#   transcoding slower is recoverable, transcoding nowhere is not.
#
# Observe Without Emby
#   With Emby down, threshold logic is skipped but the symlink is still verified. There
#   is no session pressure to react to, and acting on stale usage would flip the target
#   for sessions that no longer exist.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Wait Lock
#   acquire_lock "wait" — waits if the previous run is still active. The 7-minute
#   interval can overlap on a system under heavy load.
#
# Docker Timeout
#   DOCKER_TIMEOUT caps all docker calls against a hung daemon.
#
# Silent by Default
#   Runs every 7 minutes — only speaks when something changes or needs attention.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf
#
#   HOST*_RAMDISK_PATH / HOST*_TRANSCODE_SSD
#       Ramdisk mount point and SSD fallback path.
#       Aliased by detect_hosts().
#
#   HOST*_RAMDISK_WARN_GB / HOST*_RAMDISK_LOW_GB / HOST*_RAMDISK_SIZE
#       Thresholds and ceiling. Change all three together.
#       Aliased by detect_hosts().
#
#   HOST*_TRANSCODE_SERVERS
#       Array of media server definitions: "ContainerName|URL|APIKey|Type"
#       Type: emby | jellyfin | plex
#       Aliased by detect_hosts() → TRANSCODE_SERVERS.
#
# master.conf
#
#   TRANSCODE_MANAGER_MODE
#       smart | ramdisk | ssd. (default: smart)
#
#   TRANSCODE_CHECK_EMBY
#       Skip threshold checks when Emby not running — prevents unnecessary flips
#       overnight when no sessions are active. (default: true)
#
#   TRANSCODE_FLIP_WARN
#       Notify if symlink flips this many times in one hour — indicates ramdisk
#       is undersized for the load. (default: 3)
#
# ==============================================================================================
# STATE FILES
# ==============================================================================================
#
#   /tmp/transcode_state.db    — current symlink target, flip count, last flip time
#                                Lives in /tmp (ephemeral — resets correctly on reboot)
#   TRANSCODE_DAILY_LOG        — per-run append, read by weekly_health_digest.sh
#                                Trimmed to TRANSCODE_LOG_RETENTION days on each write
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# transcode_manager.sh
#     Check usage, flip if needed, run safety checks, display active sessions.
#
# transcode_manager.sh --dry-run
#     Show current usage and what flip decision would be made. No changes.
#
# transcode_manager.sh --status
#     Show current symlink target, ramdisk usage, session counts, and flip history.
#
# transcode_manager.sh --log
#     Verbose output including per-check results and session detail.
#
# transcode_manager.sh --no-log
#     Suppress daily log write. Used internally when called by transcode_cleanup.sh.
#
# ==============================================================================================

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

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

# Handle --no-log flag before parse_args
NO_LOG=false
FILTERED_ARGS=()
for arg in "$@"; do
    case "$arg" in
        --no-log) NO_LOG=true ;;
        *)        FILTERED_ARGS+=("$arg") ;;
    esac
done

parse_args "${FILTERED_ARGS[@]}"

DOCKER_TIMEOUT=15
STATE_FILE="${TRANSCODE_STATE_FILE:-${STATE_DIR:-/tmp}/transcode_state.db}"

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


acquire_lock "wait"

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

# detect_hosts() sets MY_ID and aliases all HOST*_TRANSCODE_* vars
detect_hosts

# Primary container — first entry in TRANSCODE_SERVERS
TRANSCODE_EMBY_CONTAINER="Emby"
if [[ "${#TRANSCODE_SERVERS[@]}" -gt 0 ]]; then
    IFS='|' read -r PRIMARY_CONTAINER _ _ _ <<< "${TRANSCODE_SERVERS[0]}"
    TRANSCODE_EMBY_CONTAINER="${PRIMARY_CONTAINER:-Emby}"
fi

case "$TRANSCODE_MANAGER_MODE" in
    smart|ramdisk|ssd) log "Mode: $TRANSCODE_MANAGER_MODE" ;;
    *)
        warn "Unknown TRANSCODE_MANAGER_MODE: $TRANSCODE_MANAGER_MODE — defaulting to smart"
        TRANSCODE_MANAGER_MODE="smart"
        ;;
esac

log "$ICON_GEAR Config: ramdisk=$RAMDISK_PATH size=$RAMDISK_SIZE warn-at=${RAMDISK_WARN_GB}GB flip-back-at=${RAMDISK_LOW_GB}GB ssd-min-free=${RAMDISK_SSD_MIN_GB}GB flip-warn=${TRANSCODE_FLIP_WARN}/hr"
log "$ICON_DISK SSD:    $TRANSCODE_SSD"

[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no changes will be made"

# ==============================================================================================
# ━━━ Status ━━━
# ==============================================================================================
if [[ "$SHOW_STATUS" == true ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY TRANSCODE MANAGER STATUS ━━━━━"
    echo "$ICON_HOST  Identity:     $MY_ID ($LOCAL_SERVER_NAME)"
    echo "$ICON_GEAR  Mode:         $TRANSCODE_MANAGER_MODE"
    echo "$ICON_RAM   Ramdisk:      $RAMDISK_PATH ($RAMDISK_SIZE)"
    echo "$ICON_LINK  Symlink:      $TRANSCODE_LINK"
    echo "$ICON_DISK  SSD:          $TRANSCODE_SSD"
    echo "$ICON_RAM   Warn at:      ${RAMDISK_WARN_GB}GB"
    echo "$ICON_RAM   Low at:       ${RAMDISK_LOW_GB}GB"
    echo "$ICON_DISK  SSD min free: ${RAMDISK_SSD_MIN_GB}GB"
    echo "$ICON_RAM   Flip warn:    $TRANSCODE_FLIP_WARN per hour"
    echo "$ICON_GEAR  Check Emby:   $TRANSCODE_CHECK_EMBY ($TRANSCODE_EMBY_CONTAINER)"
    echo "$ICON_GEAR  Dry Run:      $DRY_RUN"
    CURRENT_TARGET=$(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "missing")
    echo "$ICON_LINK  Symlink now:  $TRANSCODE_LINK → $CURRENT_TARGET"
    if mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
        USED_GB=$(df "$RAMDISK_PATH" --output=used 2>/dev/null | \
            tail -1 | tr -d ' ' | awk '{printf "%.2f", $1/1048576}')
        echo "$ICON_RAM   Ramdisk now:  ${USED_GB}GB used"
    else
        echo "$ICON_RAM   Ramdisk: NOT MOUNTED"
    fi
    echo "━━━━━━━━━━━━━━━━━━━━━━━"
    exit 0
fi

# ==============================================================================================
# ── HELPER FUNCTIONS ──────────────────────────────────────────────────────────────────────────
# ==============================================================================================

flip_symlink() {
    local target="$1" reason="$2"
    if [[ "$DRY_RUN" == true ]]; then
        warn "DRY RUN — would flip symlink to: $target ($reason)"
        return 0
    fi
    ln -sfn "$target" "$TRANSCODE_LINK"
    warn "$ICON_LINK Symlink flipped to: $target ($reason)"
}

fix_permissions() {
    local path="$1"
    [[ ! -d "$path" ]] && return
    chown -R "$TRANSCODE_OWNER" "$path" 2>/dev/null
    chmod -R "$TRANSCODE_CHMOD" "$path" 2>/dev/null
    log "Permissions fixed on $path"
}

get_ramdisk_used_gb() {
    kb_to_gb "$(df "$RAMDISK_PATH" --output=used 2>/dev/null | tail -1 | tr -d ' ')"
}

get_ramdisk_avail_gb() {
    kb_to_gb "$(df "$RAMDISK_PATH" --output=avail 2>/dev/null | tail -1 | tr -d ' ')"
}

get_ssd_free_gb() {
    kb_to_gb "$(df "$TRANSCODE_SSD" --output=avail 2>/dev/null | tail -1 | tr -d ' ')"
}

get_flip_count() {
    local state_file="${STATE_DIR:-/tmp}/transcode_flip_state.db"
    local current_hour
    current_hour=$(date '+%Y-%m-%d-%H')
    [[ ! -f "$state_file" ]] && echo "0" && return
    local stored_hour stored_count
    stored_hour=$(awk  -F'|' 'NR==1{print $1}' "$state_file" 2>/dev/null)
    stored_count=$(awk -F'|' 'NR==1{print $2}' "$state_file" 2>/dev/null)
    [[ "$stored_hour" == "$current_hour" ]] && echo "${stored_count:-0}" || echo "0"
}

increment_flip_count() {
    local state_file="${STATE_DIR:-/tmp}/transcode_flip_state.db"
    local current_hour
    current_hour=$(date '+%Y-%m-%d-%H')
    local current_count
    current_count=$(get_flip_count)
    current_count=$(( current_count + 1 ))
    echo "${current_hour}|${current_count}" > "$state_file"
    echo "$current_count"
}

get_media_type_label() {
    case "$1" in
        LiveTv|TvChannel) echo "Live TV" ;;
        Episode)          echo "TV Show" ;;
        Movie)            echo "Movie" ;;
        Audio)            echo "Music" ;;
        MusicVideo)       echo "Music Video" ;;
        *)                echo "$1" ;;
    esac
}

get_play_method_label() {
    case "$1" in
        Transcode)    echo "Transcode" ;;
        DirectStream) echo "Direct Stream" ;;
        DirectPlay)   echo "Direct Play" ;;
        *)            echo "$1" ;;
    esac
}

# ==============================================================================================
# ━━━ Transcode Manager ━━━
# ==============================================================================================
START=$(date +%s)
RAMDISK_HEALTHY=true
SSD_HEALTHY=true
EMBY_RUNNING=true
SOMETHING_HAPPENED=false   # controls whether summary is printed
ERROR_OCCURRED=false       # controls exit code

# ── Check 1 — Emby running ───────────────────────────────────────────────────────────────────
if [[ "$TRANSCODE_CHECK_EMBY" == true ]]; then
    log "Checking $TRANSCODE_EMBY_CONTAINER..."
    if ! timeout "$DOCKER_TIMEOUT" docker inspect "$TRANSCODE_EMBY_CONTAINER" \
        --format '{{.State.Running}}' 2>/dev/null | grep -q "true"; then
        warn "$TRANSCODE_EMBY_CONTAINER is not running — skipping threshold checks"
        EMBY_RUNNING=false
        SOMETHING_HAPPENED=true
    else
        log "$TRANSCODE_EMBY_CONTAINER is running ✅"
    fi
fi

# ── Check 2 — Symlink integrity ───────────────────────────────────────────────────────────────
log "Checking symlink integrity..."
CURRENT_TARGET=$(readlink "$TRANSCODE_LINK" 2>/dev/null)

if [[ -z "$CURRENT_TARGET" ]]; then
    warn "$ICON_LINK Symlink missing — recreating pointing to ramdisk"
    if [[ "$DRY_RUN" == false ]]; then
        ln -sfn "$RAMDISK_PATH" "$TRANSCODE_LINK"
        CURRENT_TARGET="$RAMDISK_PATH"
        notify "Transcode symlink was missing on $(hostname) ($MY_ID) — recreated" \
            "Transcode Manager" "warning"
    fi
    SOMETHING_HAPPENED=true
elif [[ ! -e "$CURRENT_TARGET" ]]; then
    warn "$ICON_LINK Symlink target missing: $CURRENT_TARGET — resetting to ramdisk"
    if [[ "$DRY_RUN" == false ]]; then
        ln -sfn "$RAMDISK_PATH" "$TRANSCODE_LINK"
        CURRENT_TARGET="$RAMDISK_PATH"
        notify "Transcode symlink target was missing on $(hostname) ($MY_ID) — reset to ramdisk" \
            "Transcode Manager" "warning"
    fi
    SOMETHING_HAPPENED=true
else
    log "Symlink valid: $TRANSCODE_LINK → $CURRENT_TARGET ✅"
fi

# ── Check 3 — Ramdisk health ──────────────────────────────────────────────────────────────────
log "Checking ramdisk..."

if ! mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
    error "Ramdisk not mounted at $RAMDISK_PATH"
    RAMDISK_HEALTHY=false
    SOMETHING_HAPPENED=true
    ERROR_OCCURRED=true
    if [[ "$DRY_RUN" == false ]]; then
        warn "Flipping symlink to SSD — ramdisk unavailable"
        flip_symlink "$TRANSCODE_SSD" "ramdisk disappeared"
        CURRENT_TARGET="$TRANSCODE_SSD"
        notify "Ramdisk disappeared on $(hostname) ($MY_ID) — transcodes falling back to SSD. Run ramdisk_setup.sh to restore." \
            "Transcode Manager" "warning"
    fi
else
    RAMDISK_SIZE_ACTUAL=$(df "$RAMDISK_PATH" --output=size -h 2>/dev/null | tail -1 | tr -d ' ')
    log "Ramdisk mounted — size: $RAMDISK_SIZE_ACTUAL ✅"
    fix_permissions "$RAMDISK_PATH"

    # Guarantee transcoding-temp exists on ramdisk
    TRANSCODE_TEMP_RAM="${RAMDISK_PATH}/transcoding-temp"
    if [[ ! -d "$TRANSCODE_TEMP_RAM" ]]; then
        warn "transcoding-temp missing from ramdisk — creating now"
        mkdir -p "$TRANSCODE_TEMP_RAM"
        chmod "$TRANSCODE_CHMOD" "$TRANSCODE_TEMP_RAM"
        chown "$TRANSCODE_OWNER" "$TRANSCODE_TEMP_RAM"
        warn "transcoding-temp created on ramdisk — new sessions will use ramdisk ✅"
        SOMETHING_HAPPENED=true
    else
        log "transcoding-temp exists on ramdisk ✅"
    fi
fi

# ── Check 4 — SSD health ─────────────────────────────────────────────────────────────────────
log "Checking SSD fallback..."

if [[ ! -d "$TRANSCODE_SSD" ]]; then
    warn "SSD fallback path missing: $TRANSCODE_SSD"
    SSD_HEALTHY=false
    SOMETHING_HAPPENED=true
    if [[ "$TRANSCODE_MANAGER_MODE" == "ssd" ]]; then
        error "Mode is 'ssd' but SSD path is missing — cannot continue"
        notify "Transcode SSD path missing on $(hostname) ($MY_ID) — mode is 'ssd', manual intervention needed" \
            "Transcode Manager" "warning"
        exit 1
    else
        warn "SSD fallback disabled — will stay on ramdisk"
    fi
else
    SSD_FREE_GB=$(get_ssd_free_gb)
    log "SSD available — ${SSD_FREE_GB}GB free ✅"
    fix_permissions "$TRANSCODE_SSD"
fi

# ── Usage stats ───────────────────────────────────────────────────────────────────────────────
RAMDISK_USED_GB="0.00"
RAMDISK_AVAIL_GB="0.00"
RAMDISK_FILES=0

if [[ "$RAMDISK_HEALTHY" == true ]]; then
    RAMDISK_USED_GB=$(get_ramdisk_used_gb)
    RAMDISK_AVAIL_GB=$(get_ramdisk_avail_gb)
    RAMDISK_FILES=$(find "$RAMDISK_PATH" -type f 2>/dev/null | wc -l)
fi

SSD_FILES=$(find "$TRANSCODE_SSD" -type f 2>/dev/null | wc -l)
FLIP_COUNT=$(get_flip_count)

log "Ramdisk: ${RAMDISK_USED_GB}GB used / ${RAMDISK_AVAIL_GB}GB available ($RAMDISK_FILES files)"
log "SSD:     $SSD_FILES files"
log "Flips:   $FLIP_COUNT this hour"

# ==============================================================================================
# ━━━ Active Transcode Sessions ━━━
# ==============================================================================================
TOTAL_SESSIONS=0
LIVE_TV=0
TRANSCODING=0
DIRECT=0
ANY_SERVER_RUNNING=false
RAM_SESSION_COUNT=0
SSD_SESSION_COUNT=0

for server_entry in "${TRANSCODE_SERVERS[@]}"; do
    IFS='|' read -r SRV_CONTAINER SRV_URL SRV_KEY SRV_TYPE <<< "$server_entry"

    # Skip placeholder entries
    if [[ "$SRV_KEY" == *"api-key"* ]] || \
       [[ "$SRV_KEY" == *"token"* && ${#SRV_KEY} -lt 20 ]]; then
        log "Skipping $SRV_CONTAINER — placeholder API key"
        continue
    fi

    # Container running check with timeout
    if ! timeout "$DOCKER_TIMEOUT" docker inspect "$SRV_CONTAINER" \
        --format '{{.State.Running}}' 2>/dev/null | grep -q "true"; then
        log "$SRV_CONTAINER — not running, skipping"
        continue
    fi

    ANY_SERVER_RUNNING=true

    if ! check_api "$SRV_URL" "$SRV_CONTAINER" 5; then
        warn "$SRV_CONTAINER — API unreachable, skipping"
        continue
    fi

    case "$SRV_TYPE" in
        emby|jellyfin)
            SESSION_DATA=$(curl -sf --max-time 10 \
                -H "X-Emby-Token: $SRV_KEY" \
                "${SRV_URL}/Sessions" 2>/dev/null) ;;
        plex)
            SESSION_DATA=$(curl -sf --max-time 10 \
                -H "X-Plex-Token: $SRV_KEY" \
                "${SRV_URL}/status/sessions" 2>/dev/null) ;;
        *)
            warn "Unknown server type '$SRV_TYPE' for $SRV_CONTAINER — skipping"
            continue ;;
    esac

    [[ -z "$SESSION_DATA" ]] && continue
    ! command -v jq >/dev/null 2>&1 && continue

    if [[ "$SRV_TYPE" == "emby" || "$SRV_TYPE" == "jellyfin" ]]; then
        SRV_TOTAL=$(echo "$SESSION_DATA" | \
            jq '[.[] | select(.NowPlayingItem != null)] | length' 2>/dev/null || echo 0)
        SRV_LIVE=$(echo "$SESSION_DATA" | \
            jq '[.[] | select(.NowPlayingItem != null) |
                select(.NowPlayingItem.Type == "LiveTv" or
                       .NowPlayingItem.Type == "TvChannel")] | length' \
            2>/dev/null || echo 0)
        SRV_TRANSCODE=$(echo "$SESSION_DATA" | \
            jq '[.[] | select(.NowPlayingItem != null) |
                select(.PlayState.PlayMethod == "Transcode")] | length' \
            2>/dev/null || echo 0)
        SRV_DIRECT=$(echo "$SESSION_DATA" | \
            jq '[.[] | select(.NowPlayingItem != null) |
                select(.PlayState.PlayMethod != "Transcode")] | length' \
            2>/dev/null || echo 0)

        TOTAL_SESSIONS=$(( TOTAL_SESSIONS + SRV_TOTAL ))
        LIVE_TV=$(( LIVE_TV + SRV_LIVE ))
        TRANSCODING=$(( TRANSCODING + SRV_TRANSCODE ))
        DIRECT=$(( DIRECT + SRV_DIRECT ))

        if [[ "$SRV_TOTAL" -gt 0 ]]; then
            SOMETHING_HAPPENED=true
            [[ "${#TRANSCODE_SERVERS[@]}" -gt 1 ]] && \
                echo "  $ICON_EMBY $SRV_CONTAINER — $SRV_TOTAL streams"

            while IFS= read -r session; do
                USER=$(echo "$session"  | jq -r '.UserName // "Unknown"' 2>/dev/null)
                TITLE=$(echo "$session" | jq -r '.NowPlayingItem.Name // "Unknown"' 2>/dev/null)
                MTYPE=$(echo "$session" | jq -r '.NowPlayingItem.Type // "Unknown"' 2>/dev/null)
                METH=$(echo "$session"  | jq -r '.PlayState.PlayMethod // "Unknown"' 2>/dev/null)
                echo "  $ICON_EMBY $(printf '%-12s' "$USER") — $(printf '%-30s' "$TITLE") — $(get_media_type_label "$MTYPE") — $(get_play_method_label "$METH")"
            done < <(echo "$SESSION_DATA" | jq -c '.[] | select(.NowPlayingItem != null)' 2>/dev/null)
        fi
    fi
done

# Storage state
# Sessions may use subdirectories (legacy transcode) or flat files (Live TV / Direct Stream HLS).
# Count both: subdirs per-session, and unique hex session ID prefixes for flat files.
_count_sessions() {
    local base="$1/transcoding-temp"
    local _dirs _flat
    _dirs=$(find "$base" -mindepth 1 -maxdepth 1 -type d 2>/dev/null | wc -l)
    _flat=$(find "$base" -maxdepth 1 -type f -printf '%f\n' 2>/dev/null | \
        grep -oE '^[0-9a-f]{16,}' | sort -u | wc -l)
    echo $(( _dirs + _flat ))
}
RAM_SESSION_COUNT=$(_count_sessions "$RAMDISK_PATH")
SSD_SESSION_COUNT=$(_count_sessions "$TRANSCODE_SSD")
unset -f _count_sessions

if [[ "$TOTAL_SESSIONS" -gt 0 ]]; then
    echo ""
    echo "  $ICON_EMBY Total: $TOTAL_SESSIONS  |  Live TV: $LIVE_TV  |  Transcoding: $TRANSCODING  |  Direct: $DIRECT"

    if [[ "$RAM_SESSION_COUNT" -gt 0 && "$SSD_SESSION_COUNT" -gt 0 ]]; then
        warn "Split state — $RAM_SESSION_COUNT session(s) ramdisk / $SSD_SESSION_COUNT SSD"
        warn "Older sessions remain on original location until they end naturally"
    elif [[ "$RAM_SESSION_COUNT" -gt 0 ]]; then
        log "Storage: ramdisk ($RAM_SESSION_COUNT session(s))"
    elif [[ "$SSD_SESSION_COUNT" -gt 0 ]]; then
        log "Storage: SSD ($SSD_SESSION_COUNT session(s))"
    fi
fi

[[ "$ANY_SERVER_RUNNING" == false && "$TRANSCODE_CHECK_EMBY" == true ]] && \
    { log "No configured media servers running — skipping threshold checks"; EMBY_RUNNING=false; }

# ==============================================================================================
# ━━━ Mode Logic ━━━
# ==============================================================================================
case "$TRANSCODE_MANAGER_MODE" in

    ramdisk)
        log "Mode: RAMDISK — forcing symlink to ramdisk"
        if [[ "$RAMDISK_HEALTHY" == false ]]; then
            error "Ramdisk mode selected but ramdisk is not available"
            notify "Transcode ramdisk mode failed on $(hostname) ($MY_ID) — ramdisk not mounted" \
                "Transcode Manager" "warning"
            SOMETHING_HAPPENED=true
            ERROR_OCCURRED=true
        else
            if [[ "$CURRENT_TARGET" != "$RAMDISK_PATH" ]]; then
                flip_symlink "$RAMDISK_PATH" "ramdisk mode"
                increment_flip_count > /dev/null
                FLIP_COUNT=$(get_flip_count)
                SOMETHING_HAPPENED=true
            fi
            if (( $(awk "BEGIN {print ($RAMDISK_USED_GB >= $RAMDISK_WARN_GB) ? 1 : 0}") )); then
                warn "Ramdisk usage ${RAMDISK_USED_GB}GB above threshold ${RAMDISK_WARN_GB}GB — consider switching to smart mode"
                notify "Ramdisk high on $(hostname) ($MY_ID) — ${RAMDISK_USED_GB}GB in ramdisk-only mode" \
                    "Transcode Manager" "warning"
                SOMETHING_HAPPENED=true
            fi
        fi
        ;;

    ssd)
        log "Mode: SSD — forcing symlink to SSD"
        if [[ "$SSD_HEALTHY" == false ]]; then
            error "SSD mode selected but SSD path is not available"
            SOMETHING_HAPPENED=true
            ERROR_OCCURRED=true
        else
            if [[ "$CURRENT_TARGET" != "$TRANSCODE_SSD" ]]; then
                flip_symlink "$TRANSCODE_SSD" "ssd mode"
                increment_flip_count > /dev/null
                FLIP_COUNT=$(get_flip_count)
                SOMETHING_HAPPENED=true
            fi
        fi
        ;;

    smart)
        log "Mode: SMART — auto threshold management"

        if [[ "$EMBY_RUNNING" == false ]]; then
            log "No media servers running — skipping threshold checks"

        elif [[ "$RAMDISK_HEALTHY" == false ]]; then
            log "Ramdisk unavailable — staying on SSD until ramdisk recovers"

        elif [[ "$CURRENT_TARGET" == "$RAMDISK_PATH" ]]; then
            if (( $(awk "BEGIN {print ($RAMDISK_USED_GB >= $RAMDISK_WARN_GB) ? 1 : 0}") )); then
                if [[ "$SSD_HEALTHY" == false ]]; then
                    error "Ramdisk above threshold but SSD unavailable — cannot flip"
                    notify "Transcode ramdisk full on $(hostname) ($MY_ID) and SSD unavailable" \
                        "Transcode Manager" "warning"
                    SOMETHING_HAPPENED=true
                    ERROR_OCCURRED=true
                else
                    SSD_FREE_GB=$(get_ssd_free_gb)
                    if (( $(awk "BEGIN {print ($SSD_FREE_GB < $RAMDISK_SSD_MIN_GB) ? 1 : 0}") )); then
                        warn "SSD only ${SSD_FREE_GB}GB free — below ${RAMDISK_SSD_MIN_GB}GB minimum, not flipping"
                        SOMETHING_HAPPENED=true
                        ERROR_OCCURRED=true
                    else
                        warn "Ramdisk ${RAMDISK_USED_GB}GB — above ${RAMDISK_WARN_GB}GB, flipping to SSD"
                        flip_symlink "$TRANSCODE_SSD" "threshold exceeded"
                        CURRENT_TARGET="$TRANSCODE_SSD"
                        NEW_COUNT=$(increment_flip_count)
                        FLIP_COUNT=$NEW_COUNT
                        SOMETHING_HAPPENED=true
                        if [[ "$NEW_COUNT" -ge "$TRANSCODE_FLIP_WARN" ]]; then
                            notify "Transcode flipped to SSD on $(hostname) ($MY_ID) — ${NEW_COUNT} flips this hour — consider increasing HOST*_RAMDISK_SIZE" \
                                "Transcode Manager" "warning"
                        fi
                    fi
                fi
            else
                log "Ramdisk ${RAMDISK_USED_GB}GB — below threshold — no action needed"
            fi

        elif [[ "$CURRENT_TARGET" == "$TRANSCODE_SSD" ]]; then
            if (( $(awk "BEGIN {print ($RAMDISK_USED_GB <= $RAMDISK_LOW_GB) ? 1 : 0}") )); then
                warn "Ramdisk ${RAMDISK_USED_GB}GB — below ${RAMDISK_LOW_GB}GB, flipping back to ramdisk"
                flip_symlink "$RAMDISK_PATH" "usage recovered"
                CURRENT_TARGET="$RAMDISK_PATH"
                NEW_COUNT=$(increment_flip_count)
                FLIP_COUNT=$NEW_COUNT
                SOMETHING_HAPPENED=true
            else
                log "On SSD — ramdisk ${RAMDISK_USED_GB}GB still above low threshold ${RAMDISK_LOW_GB}GB"
            fi
        fi
        ;;
esac

END=$(date +%s)
CURRENT_TARGET=$(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "unknown")

# ==============================================================================================
# ━━━ Daily Log Write ━━━
# ==============================================================================================
# Read by weekly_health_digest.sh — format: DATE|USED_GB|FLIPS|RAM_SESSIONS|SSD_SESSIONS
if [[ "$DRY_RUN" == false && "$NO_LOG" == false && -n "${TRANSCODE_DAILY_LOG:-}" ]]; then
    TODAY=$(date '+%Y-%m-%d')
    mkdir -p "$(dirname "$TRANSCODE_DAILY_LOG")"
    echo "${TODAY}|${RAMDISK_USED_GB}|${FLIP_COUNT}|${RAM_SESSION_COUNT}|${SSD_SESSION_COUNT}" \
        >> "$TRANSCODE_DAILY_LOG" 2>/dev/null || true
    log "Daily log written: $TRANSCODE_DAILY_LOG"

    # Trim entries older than TRANSCODE_LOG_RETENTION days
    LOG_CUTOFF=$(date -d "${TRANSCODE_LOG_RETENTION:-90} days ago" '+%Y-%m-%d' 2>/dev/null)
    if [[ -n "$LOG_CUTOFF" ]]; then
        awk -F'|' -v cutoff="$LOG_CUTOFF" '$1 >= cutoff' "$TRANSCODE_DAILY_LOG" \
            > "${TRANSCODE_DAILY_LOG}.tmp" 2>/dev/null && \
            mv "${TRANSCODE_DAILY_LOG}.tmp" "$TRANSCODE_DAILY_LOG"
    fi
fi

# ==============================================================================================
# ━━━ Summary — only shown when something happened ━━━
# ==============================================================================================
if [[ "$SOMETHING_HAPPENED" == true ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY TRANSCODE MANAGER SUMMARY ━━━━━"
    echo "$ICON_HOST  Identity:  $MY_ID ($LOCAL_SERVER_NAME)"
    echo "$ICON_GEAR  Mode:      $TRANSCODE_MANAGER_MODE"
    echo "$ICON_RAM   Ramdisk:   ${RAMDISK_USED_GB}GB used / ${RAMDISK_AVAIL_GB}GB available"
    echo "$ICON_LINK  Symlink:   $TRANSCODE_LINK → $CURRENT_TARGET"
    echo "$ICON_LINK  Flips:     $FLIP_COUNT this hour (warn at $TRANSCODE_FLIP_WARN)"
    echo "$ICON_EMBY  Streams:   $TOTAL_SESSIONS total  |  Live TV: $LIVE_TV  |  Transcoding: $TRANSCODING  |  Direct: $DIRECT"
    echo "$ICON_TIME  Duration:  $(format_duration $(( END - START )))"
    echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
else
    echo "Transcode manager — all healthy — $MY_ID ($(format_duration $(( END - START ))))"
fi

[[ "$ERROR_OCCURRED" == true ]] && exit 1
exit 0