#!/bin/bash
# ==============================================================================================
# =============================== Emby → Lidarr Sync ===========================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# One-shot tool. Finds artists played on Emby that are not tracked in Lidarr
# and adds them. No scoring — if you played it, Lidarr should monitor it.
#
# Intended as a bootstrap / catch-up tool, not a scheduled script. Run it once
# after Lidarr is set up, or any time you suspect gaps between what you listen
# to and what Lidarr monitors.
#
# Cache-first Lidarr library fetch (2026-07-17) — comes from the shared tracked-data cache
# via arr_get_tracked_data(), fresh (kept warm every 30min by arr_cache_prefill.sh), live
# fetch as fallback.
#
# ==============================================================================================
# OPERATIONAL MODEL
# ==============================================================================================
#
#   1. Pull play completions from Emby activity log (all time, or --days N)
#   2. Fetch current Lidarr artist library
#   3. For each played artist not in Lidarr → add to Lidarr
#
# ==============================================================================================
# DESIGN PRINCIPLES
# ==============================================================================================
#
# Add-Only, Never Remove
#   The tool only adds artists the arr is missing. It never deletes or unmonitors anything,
#   so the worst outcome of a bad match is an extra tracked entry — trivially reversible —
#   rather than lost tracking on something already curated.
#
# Library Membership Is the Whole Criterion
#   No scoring, no thresholds, no metadata quality gates. If it is in Emby, the arr should
#   know about it. This is deliberately not a discovery tool; the playback_aware_* scripts
#   own that job and its judgement calls.
#
# Provider ID Over Title
#   Matching prefers the MusicBrainz ID and falls back to case-insensitive title only when the ID
#   is absent. Titles differ across sources by punctuation, year suffixes and articles;
#   matching on them alone would re-add things the arr already tracks.
#
# Cache-First Read
#   The arr library comes from the shared tracked-data cache, kept warm by
#   arr_cache_prefill.sh, with a live fetch as fallback. One read regardless of library size.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Root Required
#   acquire_lock requires root. Script exits cleanly if not root.
#
# Dry-Run Mode
#   --dry-run shows all artists that would be added without making any Lidarr API calls.
#   Always run first when closing the gap after a fresh Lidarr install or database wipe.
#
# Add-Only
#   Only adds artists to Lidarr. Items already tracked (by name) are skipped without
#   modification. Safe to run multiple times — the second run finds nothing to add.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
#   HOST*_LIDARR_URL / HOST*_LIDARR_API_KEY    — Lidarr connection (aliased by detect_hosts)
#   HOST*_EMBY_URL   / HOST*_EMBY_API_KEY      — Emby connection (aliased by detect_hosts)
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
#   emby_to_lidarr_sync.sh             — add all album artists not in Lidarr
#   emby_to_lidarr_sync.sh --dry-run   — show what would be added, no changes
#   emby_to_lidarr_sync.sh --log       — verbose output
#
# ==============================================================================================

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

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

parse_args "$@"

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

acquire_lock

detect_hosts

if ! command -v jq >/dev/null 2>&1; then
    error "jq not installed"
    exit 1
fi

if [[ -z "${EMBY_URL:-}" || -z "${EMBY_API_KEY:-}" ]]; then
    error "EMBY_URL / EMBY_API_KEY not configured — check host*.conf"
    exit 1
fi

if [[ -z "${LIDARR_URL:-}" || -z "${LIDARR_API_KEY:-}" ]]; then
    error "LIDARR_URL / LIDARR_API_KEY not configured — check host*.conf"
    exit 1
fi


log "$ICON_GEAR Config: emby=${EMBY_URL} lidarr=${LIDARR_URL}"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no artists will be added to Lidarr"

# ==============================================================================================
# ── API HELPERS ───────────────────────────────────────────────────────────────────────────────
# ==============================================================================================


_lidarr_get() {
    curl -sf --max-time 30 \
        -H "X-Api-Key: $LIDARR_API_KEY" \
        "${LIDARR_URL}/api/v1/${1}" 2>/dev/null
}

_lidarr_lookup() {
    curl -sf --max-time 20 --get \
        --data-urlencode "term=$1" \
        -H "X-Api-Key: $LIDARR_API_KEY" \
        "${LIDARR_URL}/api/v1/artist/lookup" 2>/dev/null
}

_lidarr_post() {
    curl -sf --max-time 20 -X POST \
        -H "X-Api-Key: $LIDARR_API_KEY" \
        -H "Content-Type: application/json" \
        -d "$1" \
        "${LIDARR_URL}/api/v1/artist" 2>/dev/null
}

_lidarr_command() {
    curl -sf --max-time 20 -X POST \
        -H "X-Api-Key: $LIDARR_API_KEY" \
        -H "Content-Type: application/json" \
        -d "$1" \
        "${LIDARR_URL}/api/v1/command" 2>/dev/null
}

_is_placeholder_artist() {
    local a="${1,,}"
    [[ "$a" =~ ^(va|various|various artists|unknown artist|unknown|soundtrack|original soundtrack|ost)$ ]]
}

# Dirty tag: comma-list, feat./ft., ampersand join, or "Artist - Album" in the AlbumArtist field
_is_dirty_artist() {
    local lower="${1,,}"
    [[ "$1" == *", "* ]]       && return 0
    [[ "$lower" == *" feat."* ]] && return 0
    [[ "$lower" == *" ft."* ]]   && return 0
    [[ "$1" == *" & "* ]]      && return 0
    [[ "$lower" == *" vs "* ]] && return 0
    [[ "$1" == *" - "* ]]      && return 0
    [[ "$1" == *"?"* ]]        && return 0   # ASCII ? — encoding corruption
    [[ "$1" == *$'\xef\xbf\xbd'* ]] && return 0   # U+FFFD replacement character — encoding corruption
    return 1
}

# ==============================================================================================
# ━━━ Fetch Emby Music Artist Library ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY Emby → Lidarr Sync — $(date '+%Y-%m-%d %H:%M:%S') ━━━━━"
echo "$ICON_HOST $MY_ID ($LOCAL_SERVER_NAME)"
[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no artists will be added"

echo ""
echo "━━━ $ICON_SYNC Emby Music Library ━━━"

# Query MusicAlbum items and extract AlbumArtists — gives primary album artists only,
# not the full tag credits (guest features, songwriters, etc.) that MusicArtist returns
EMBY_ALBUMS_JSON=$(emby_api "Items?IncludeItemTypes=MusicAlbum&Recursive=true&Fields=AlbumArtists&Limit=10000") || {
    error "Could not fetch Emby music albums"
    exit 1
}

declare -A PLAYED_ARTISTS

while IFS= read -r artist; do
    [[ -z "$artist" || "$artist" == "null" ]] && continue
    _is_placeholder_artist "$artist" && continue
    _is_dirty_artist "$artist" && { log "  $ICON_SKIP Skipping dirty tag: $artist"; continue; }
    PLAYED_ARTISTS["$artist"]=1
done < <(echo "$EMBY_ALBUMS_JSON" | jq -r '.Items[] | .AlbumArtists[]?.Name' 2>/dev/null)

PLAYED_COUNT=${#PLAYED_ARTISTS[@]}
log "$PLAYED_COUNT album artists in Emby library"

if [[ "$PLAYED_COUNT" -eq 0 ]]; then
    warn "No album artists found in Emby library"
    exit 0
fi

# ==============================================================================================
# ━━━ Fetch Lidarr Library ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_SYNC Lidarr Library ━━━"

# Cache-first — arr_get_tracked_data() serves the shared cache when it's fresh (kept current
# every 30min by arr_cache_prefill.sh in CRITICAL_MAINTENANCE_SCRIPTS), falls back to a live
# fetch when it's stale, and waits out an active rescan before either.
LIDARR_ARTISTS_JSON=$(arr_get_tracked_data "lidarr" "$LIDARR_URL" "$LIDARR_API_KEY" "v1") || { error "Could not fetch Lidarr artists"; exit 1; }
LIDARR_NAMES=$(echo "$LIDARR_ARTISTS_JSON" | jq -r '.[].artistName' 2>/dev/null)
LIDARR_COUNT=$(echo "$LIDARR_NAMES" | grep -c . 2>/dev/null || echo 0)
log "$LIDARR_COUNT artists already in Lidarr"

_in_lidarr() {
    echo "$LIDARR_NAMES" | grep -iq "^${1}$"
}

# ==============================================================================================
# ━━━ Find Gaps ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_GEAR Comparing Libraries ━━━"

MISSING=()
ALREADY=0

for artist in "${!PLAYED_ARTISTS[@]}"; do
    if _in_lidarr "$artist"; then
        (( ALREADY++ ))
        log "  $ICON_SKIP Already tracked: $artist"
    else
        MISSING+=("$artist")
        log "  $ICON_WARN Not in Lidarr: $artist"
    fi
done

IFS=$'\n' MISSING=($(printf '%s\n' "${MISSING[@]}" | sort))

echo "  Already tracked: $ALREADY | Missing from Lidarr: ${#MISSING[@]}"

if [[ "${#MISSING[@]}" -eq 0 ]]; then
    echo "Lidarr already tracks everything played on Emby"
    exit 0
fi

echo ""
echo "━━━ $ICON_SUMMARY Artists to add (${#MISSING[@]}) ━━━"
for a in "${MISSING[@]}"; do echo "  $a"; done

if [[ "$DRY_RUN" == true ]]; then
    warn "DRY RUN complete — run without --dry-run to add these artists"
    exit 0
fi

# ==============================================================================================
# ━━━ Add to Lidarr ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_SYNC Adding to Lidarr ━━━"

LIDARR_ROOT=$(_lidarr_get "rootfolder" | jq -r 'first(.[] | select(.accessible == true)) | .path' 2>/dev/null)
if [[ -z "$LIDARR_ROOT" ]]; then error "Could not determine Lidarr root folder"; exit 1; fi

QUALITY_PROFILES=$(_lidarr_get "qualityprofile") || { error "Could not fetch quality profiles"; exit 1; }
METADATA_PROFILES=$(_lidarr_get "metadataprofile") || { error "Could not fetch metadata profiles"; exit 1; }

DEFAULT_QUALITY_ID=$(echo "$QUALITY_PROFILES" | jq -r '.[0].id' 2>/dev/null)
DEFAULT_METADATA_ID=$(echo "$METADATA_PROFILES" | jq -r '
    first(.[] | select(.name | test("Standard"; "i")) | .id) // .[0].id' 2>/dev/null)

log "Quality profile: $DEFAULT_QUALITY_ID | Metadata profile: $DEFAULT_METADATA_ID"

ADDED=0
FAILED=0
SKIPPED=0

for artist in "${MISSING[@]}"; do
    LOOKUP=$(_lidarr_lookup "$artist")

    if [[ -z "$LOOKUP" ]] || echo "$LOOKUP" | jq -e '. == [] or . == null' >/dev/null 2>&1; then
        warn "  $ICON_WARN No match in Lidarr lookup: $artist"
        (( FAILED++ ))
        continue
    fi

    ARTIST_DATA=$(echo "$LOOKUP" | jq '.[0]' 2>/dev/null)
    MBID=$(echo "$ARTIST_DATA" | jq -r '.foreignArtistId // ""' 2>/dev/null)
    LIDARR_NAME=$(echo "$ARTIST_DATA" | jq -r '.artistName // ""' 2>/dev/null)

    if [[ -z "$MBID" ]]; then
        warn "  $ICON_WARN No MusicBrainz ID for: $artist"
        (( FAILED++ ))
        continue
    fi

    PAYLOAD=$(echo "$ARTIST_DATA" | jq \
        --arg root "$LIDARR_ROOT" \
        --argjson qid "$DEFAULT_QUALITY_ID" \
        --argjson mid "$DEFAULT_METADATA_ID" \
        '. + {
            rootFolderPath: $root,
            qualityProfileId: $qid,
            metadataProfileId: $mid,
            monitored: true,
            addOptions: {
                monitor: "all",
                searchForMissingAlbums: false
            }
        }' 2>/dev/null)

    RESULT=$(_lidarr_post "$PAYLOAD")

    if echo "$RESULT" | jq -e '.id' >/dev/null 2>&1; then
        ARTIST_ID=$(echo "$RESULT" | jq -r '.id')
        _lidarr_command "{\"name\":\"ArtistSearch\",\"artistId\":${ARTIST_ID}}" >/dev/null
        log "  $ICON_DONE Added: $LIDARR_NAME"
        (( ADDED++ ))
    else
        warn "  $ICON_WARN Failed to add: $artist"
        log "  $(echo "$RESULT" | head -c 200)"
        (( FAILED++ ))
    fi
done

# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo ""
echo "━━━━━ $ICON_SUMMARY SYNC COMPLETE ━━━━━"
echo "  $ICON_DONE  Added:   $ADDED"
[[ "$FAILED" -gt 0 ]] && echo "  $ICON_WARN  Failed:  $FAILED"
echo "  $ICON_SKIP  Already tracked: $ALREADY"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

[[ "$ADDED" -gt 0 ]] && notify \
    "$ADDED artist(s) added to Lidarr from Emby play history on $(hostname)" \
    "Emby → Lidarr Sync" "normal"

exit 0
