#!/bin/bash
# ==============================================================================================
# ============================= Emby Session Report ============================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Emby usage report via the Emby API. Scheduled weekly (Sunday 11am). Queries
# activity logs and session history to produce a summary of what was watched,
# by whom, and how often. No persistent state — queries fresh on every run.
#
# Reports: server info and uptime, active sessions and transcode ratio, library
# counts (movies/episodes/songs), activity history for the last EMBY_REPORT_DAYS
# days, top EMBY_REPORT_TOP_N content items, most active users, and ramdisk
# transcode status.
#
# Notifies only if transcoding exceeds 80% of streams — may indicate a client
# configuration issue. Silent on clean runs.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
#   1. Reachability — Emby API responding; unreachable exits cleanly rather than
#      reporting an empty library as a real result
#   2. Server info and uptime
#   3. Active sessions — count, and the transcode-to-direct-play ratio
#   4. Library counts — movies, episodes, songs
#   5. Activity history over the last EMBY_REPORT_DAYS
#   6. Top EMBY_REPORT_TOP_N items and most active users
#   7. Ramdisk transcode status, read from the shared transcode state
#
# Every figure is queried fresh. The only notification is the transcode-ratio warning;
# everything else is report output.
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# No Persistent State
#   Every report is generated fresh from the Emby API. No local database, no
#   incremental tracking. A missed run leaves no gap — the next run simply
#   covers its own window.
#
# Silent When Healthy
#   The report goes to Discord/notification as a summary. Transcode alerts are
#   the only proactive notification — high transcode ratios may indicate a
#   misconfigured client that needs attention before it becomes a performance issue.
#
# Section Independence
#   Each report section (sessions, library, activity, top content) guards its own
#   API calls. A failure in one section does not abort the others — the report
#   produces partial output rather than nothing.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Single Instance Lock
#   acquire_lock prevents duplicate reports running simultaneously.
#
# API Connectivity Check
#   check_api() verifies Emby is reachable before any queries. API failure in
#   one section does not abort the others — each section guards itself.
#
# Tool Validation
#   Checks for curl and jq at startup — exits with a clear error if either is missing.
#
# Per-Host Credentials
#   detect_hosts() aliases HOST*_EMBY_URL and HOST*_EMBY_API_KEY → EMBY_URL / EMBY_API_KEY.
#   Each server reports on its own Emby instance automatically.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf
#
#   HOST*_EMBY_URL
#       Emby server URL for this host. Aliased by detect_hosts() → EMBY_URL.
#
#   HOST*_EMBY_API_KEY
#       Emby API key for this host. Aliased by detect_hosts() → EMBY_API_KEY.
#       Generate via Emby UI → Settings → API Keys → New API Key.
#
# master.conf
#
#   EMBY_REPORT_DAYS
#       Number of days to include in the activity history section. (default: 7)
#
#   EMBY_REPORT_TOP_N
#       Number of top content items to show in the report. (default: 10)
#
#   RAMDISK_PATH
#       Ramdisk mount path — used for transcode status reporting.
#
#   TRANSCODE_LINK
#       Symlink path — used to determine current transcode location.
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# emby_session_report.sh
#     Generate and send Emby usage report.
#
# emby_session_report.sh --dry-run
#     Test API connectivity and generate report output. No notification sent.
#
# emby_session_report.sh --status
#     Show Emby URL, API key (masked), and report configuration. Then exit.
#
# emby_session_report.sh --log
#     Verbose per-section output during report generation.
#
# ==============================================================================================

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

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

parse_args "$@"

# ==============================================================================================
# ━━━ Setup ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_GEAR Setup ━━━"

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

if ! command -v curl >/dev/null 2>&1; then
    error "curl not found — required for Emby API calls"
    exit 1
fi

if ! command -v jq >/dev/null 2>&1; then
    error "jq not found — required for JSON parsing"
    notify "Emby report failed on $(hostname) — jq not installed" "Emby Report" "warning"
    exit 1
fi


acquire_lock

# detect_hosts() sets MY_ID and aliases EMBY_URL, EMBY_API_KEY
detect_hosts

require_var EMBY_URL
require_var EMBY_API_KEY

log "$ICON_GEAR Config: url=${EMBY_URL} period=${EMBY_REPORT_DAYS}d top=${EMBY_REPORT_TOP_N}"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — API queried but no notification sent"

# ==============================================================================================
# ━━━ Status ━━━
# ==============================================================================================
if [[ "$SHOW_STATUS" == true ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
    echo "$ICON_HOST    Identity:    $MY_ID ($LOCAL_SERVER_NAME)"
    echo "$ICON_EMBY    Emby URL:    $EMBY_URL"
    echo "$ICON_TIME    Period:      Last ${EMBY_REPORT_DAYS} days"
    echo "$ICON_EMBY    Top N:       ${EMBY_REPORT_TOP_N} items"
    echo "$ICON_GEAR    Dry Run:     $DRY_RUN"
    echo "━━━━━━━━━━━━━━━━━━━━━━━"
    exit 0
fi

# ==============================================================================================
# ── API HELPER ────────────────────────────────────────────────────────────────────────────────
# ==============================================================================================
emby_api() {
    local endpoint="$1"
    local response http_code body

    response=$(curl -sf \
        --max-time 15 \
        -H "X-Emby-Token: $EMBY_API_KEY" \
        -w "\n%{http_code}" \
        "${EMBY_URL}/${endpoint}" 2>/dev/null)

    http_code=$(echo "$response" | tail -1)
    body=$(echo "$response" | head -n -1)

    if [[ "$http_code" != "200" ]]; then
        error "Emby API HTTP $http_code for: $endpoint"
        return 1
    fi
    echo "$body"
}

# ==============================================================================================
# ━━━ Emby Session Report ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_EMBY Emby Session Report — $(date '+%Y-%m-%d %H:%M:%S') ━━━"
echo "$ICON_HOST  $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_TIME  Period: Last ${EMBY_REPORT_DAYS} days"
echo ""

START=$(date +%s)

# ── Connectivity and server info ──────────────────────────────────────────────────────────────
if ! check_api "$EMBY_URL" "Emby" 10; then
    notify "Emby report failed on $(hostname) — cannot connect to Emby at $EMBY_URL" \
        "Emby Report" "warning"
    exit 1
fi

SYSTEM_INFO=$(emby_api "System/Info" 2>/dev/null) || {
    error "Cannot connect to Emby at $EMBY_URL"
    exit 1
}

SERVER_NAME=$(echo "$SYSTEM_INFO" | jq -r '.ServerName // "Unknown"' 2>/dev/null)
SERVER_VERSION=$(echo "$SYSTEM_INFO" | jq -r '.Version // "Unknown"' 2>/dev/null)
echo "$ICON_EMBY Connected to: $SERVER_NAME (v$SERVER_VERSION) ✅"

# ── Active Sessions ───────────────────────────────────────────────────────────────────────────
echo "━━━ $ICON_EMBY Active Sessions ━━━"
SESSIONS=$(emby_api "Sessions" 2>/dev/null) || { warn "Could not fetch sessions"; SESSIONS="[]"; }

ACTIVE_COUNT=$(echo "$SESSIONS" | \
    jq '[.[] | select(.NowPlayingItem != null)] | length' 2>/dev/null || echo 0)
TRANSCODE_NOW=$(echo "$SESSIONS" | \
    jq '[.[] | select(.NowPlayingItem != null) | select(.TranscodingInfo != null)] | length' \
    2>/dev/null || echo 0)
DIRECT_NOW=$(( ACTIVE_COUNT - TRANSCODE_NOW ))

TRANSCODE_PCT=0
[[ "$ACTIVE_COUNT" -gt 0 ]] && TRANSCODE_PCT=$(( TRANSCODE_NOW * 100 / ACTIVE_COUNT ))

echo "  $ICON_EMBY Active streams:  $ACTIVE_COUNT"
echo "  $ICON_EMBY Direct play:     $DIRECT_NOW"
echo "  $ICON_EMBY Transcoding:     $TRANSCODE_NOW"

if [[ "$ACTIVE_COUNT" -gt 0 ]]; then
    echo ""
    echo "  Now playing:"
    echo "$SESSIONS" | jq -r '
        .[] |
        select(.NowPlayingItem != null) |
        "    \(.UserName // "Unknown") → \(.NowPlayingItem.Name // "Unknown") [\(if .TranscodingInfo != null then "transcode" else "direct" end)]"
    ' 2>/dev/null || true
fi
log "$ICON_EMBY Sessions: $ACTIVE_COUNT active ($DIRECT_NOW direct / $TRANSCODE_NOW transcode)"
echo ""

# ── Library Stats ─────────────────────────────────────────────────────────────────────────────
echo "━━━ $ICON_EMBY Library ━━━"
ITEMS=$(emby_api "Items/Counts" 2>/dev/null) || { warn "Could not fetch library counts"; ITEMS="{}"; }

MOVIE_COUNT=$(echo "$ITEMS" | jq '.MovieCount // 0' 2>/dev/null || echo 0)
EPISODE_COUNT=$(echo "$ITEMS" | jq '.EpisodeCount // 0' 2>/dev/null || echo 0)
SONG_COUNT=$(echo "$ITEMS" | jq '.SongCount // 0' 2>/dev/null || echo 0)

echo "  $ICON_EMBY Movies:    $MOVIE_COUNT"
echo "  $ICON_EMBY Episodes:  $EPISODE_COUNT"
echo "  $ICON_EMBY Songs:     $SONG_COUNT"
log "$ICON_EMBY Library: $MOVIE_COUNT movies  $EPISODE_COUNT episodes  $SONG_COUNT songs"
echo ""

# ── Activity History ──────────────────────────────────────────────────────────────────────────
# Query activity log for the configured period
echo "━━━ $ICON_EMBY Activity — Last ${EMBY_REPORT_DAYS} Days ━━━"

REPORT_START=$(date -d "${EMBY_REPORT_DAYS} days ago" '+%Y-%m-%dT00:00:00.000Z')

ACTIVITY=$(emby_api "System/ActivityLog/Entries?MinDate=${REPORT_START}&Limit=1000" \
    2>/dev/null) || { warn "Could not fetch activity log"; ACTIVITY="{}"; }

# Emby 4.9 event types changed: VideoPlayback → playback.stop (completed session)
TOTAL_PLAYS=$(echo "$ACTIVITY" | \
    jq '[.Items // [] | .[] | select(.Type == "playback.stop")] | length' \
    2>/dev/null || echo 0)

echo "  $ICON_EMBY Total play events:  $TOTAL_PLAYS"
[[ "$TOTAL_PLAYS" -gt 0 ]] && log "$ICON_EMBY Activity: $TOTAL_PLAYS plays"
echo ""

# ── Top Content ───────────────────────────────────────────────────────────────────────────────
echo "━━━ $ICON_EMBY Top ${EMBY_REPORT_TOP_N} Content ━━━"

# Emby 4.9 requires a UserId on the Items endpoint — fetch admin ID first
EMBY_ADMIN_ID=$(emby_api "Users" 2>/dev/null | \
    jq -r '[.[] | select(.Policy.IsAdministrator == true)] | first | .Id // empty' 2>/dev/null)
if [[ -z "$EMBY_ADMIN_ID" ]]; then
    warn "Could not resolve admin UserId — skipping top content"
    TOP_ITEMS="{}"
else
    TOP_ITEMS=$(emby_api "Users/${EMBY_ADMIN_ID}/Items?SortBy=DatePlayed&SortOrder=Descending&Limit=${EMBY_REPORT_TOP_N}&Recursive=true&Fields=Overview&IncludeItemTypes=Movie,Episode" \
        2>/dev/null) || { warn "Could not fetch top content"; TOP_ITEMS="{}"; }
fi

TOP_COUNT=$(echo "$TOP_ITEMS" | jq '.Items // [] | length' 2>/dev/null || echo 0)
if [[ "$TOP_COUNT" -gt 0 ]]; then
    echo "$TOP_ITEMS" | jq -r '
        .Items // [] |
        to_entries[] |
        "  \(.key + 1). \(.value.Name // "Unknown") [\(.value.Type // "")]"
    ' 2>/dev/null || warn "Could not parse top content"
else
    echo "  No recent play history found"
fi
echo ""

# ── Most Active Users ─────────────────────────────────────────────────────────────────────────
echo "━━━ $ICON_EMBY Most Active Users ━━━"
USERS=$(emby_api "Users" 2>/dev/null) || { warn "Could not fetch users"; USERS="[]"; }

USER_COUNT=$(echo "$USERS" | jq 'length' 2>/dev/null || echo 0)
echo "  $ICON_EMBY Total users: $USER_COUNT"

if [[ "$USER_COUNT" -gt 0 ]]; then
    echo "$USERS" | jq -r '
        sort_by(.LastActivityDate // "0") |
        reverse |
        .[:5][] |
        "  \(.Name // "Unknown") — last active: \(.LastActivityDate // "never" | split("T")[0])"
    ' 2>/dev/null || true
fi
echo ""

# ── Ramdisk / Transcode Status ────────────────────────────────────────────────────────────────
echo "━━━ $ICON_RAM Transcode Status ━━━"
if mountpoint -q "$RAMDISK_PATH" 2>/dev/null; then
    RAMDISK_USED_KB=$(df "$RAMDISK_PATH" --output=used 2>/dev/null | tail -1 | tr -d ' ')
    RAMDISK_USED_GB=$(kb_to_gb "$RAMDISK_USED_KB")
    SYMLINK=$(readlink "$TRANSCODE_LINK" 2>/dev/null || echo "unknown")
    echo "  $ICON_RAM  Ramdisk usage:  ${RAMDISK_USED_GB}GB / ${RAMDISK_SIZE:-8G}"
    echo "  $ICON_LINK Symlink target: $SYMLINK"
    if [[ "$SYMLINK" == *"ssd"* ]] || [[ "$SYMLINK" == *"cache"* ]]; then
        warn "Transcode link pointing at SSD — ramdisk may be full"
    fi
else
    warn "Ramdisk not mounted at $RAMDISK_PATH"
fi
echo ""

END=$(date +%s)

# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo "━━━━━ $ICON_SUMMARY EMBY REPORT SUMMARY ━━━━━"
echo "$ICON_HOST    Identity:  $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_EMBY    Server:    $SERVER_NAME (v$SERVER_VERSION)"
echo "$ICON_EMBY    Active:    $ACTIVE_COUNT streams ($DIRECT_NOW direct / $TRANSCODE_NOW transcode, ${TRANSCODE_PCT}%)"
echo "$ICON_EMBY    Library:   $MOVIE_COUNT movies  $EPISODE_COUNT episodes  $SONG_COUNT songs"
echo "$ICON_EMBY    Period:    $TOTAL_PLAYS play events in last ${EMBY_REPORT_DAYS} days"
echo "$ICON_TIME    Duration:  $(format_duration $(( END - START )))"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

# Only notify on issues — high transcode rate may indicate config problem
if [[ "$DRY_RUN" == false ]]; then
    if [[ "$TOTAL_PLAYS" -gt 10 && "$TRANSCODE_PCT" -gt 80 ]]; then
        notify "Emby report on $(hostname) — high transcode rate: ${TRANSCODE_PCT}% of $TOTAL_PLAYS plays — check direct play config" \
            "Emby Report" "warning"
    fi
fi

exit 0