#!/bin/bash
# ==============================================================================================
# ================================= Docker Weekly Restart ======================================
# ==============================================================================================
#
# PURPOSE
# ─────────────────────────────────────────────────────────────────────────────
# Restarts configured containers every Sunday at 2:30am as proactive maintenance.
#
# Called by weekly_sync_maintenance.sh via WEEKLY_MAINTENANCE_SCRIPTS. Runs after
# the sync window has completed and already restarted its own critical containers
# (Emby, auth stack). Targets a separate set of less-critical services that benefit
# from a weekly clean start but do not need to be stopped for the sync itself.
#
# Same behavioural rules as docker_daily_restart.sh: running → restart,
# stopped → leave, missing → skip. Container state is always respected.
#
# ==============================================================================================
# OPERATIONAL SAFEGUARDS
# ==============================================================================================
#
# Dependency Ordering
#   Containers restart in dependency-safe order using HOST*_WATCHDOG_DEPENDENCIES.
#   CONTAINER_DELAY seconds between dependency restart and dependent restart.
#
# Restart Verification
#   Container state checked after a settle period. A container that crashes
#   immediately after restart is marked failed with a notification sent.
#
# Timeout Protection
#   All docker commands wrapped in a 30 second timeout. A hung Docker daemon
#   cannot cause this script to hang indefinitely.
#
# Host Detection
#   detect_hosts() identifies which server is running the script and aliases
#   HOST*_WEEKLY_RESTART_CONTAINERS and HOST*_WATCHDOG_DEPENDENCIES to the
#   correct host's values.
#
# Lock Acquisition
#   acquire_lock() prevents concurrent execution.
#
# ==============================================================================================
# CONFIGURATION
# ==============================================================================================
#
# host*.conf
#
#   HOST*_WEEKLY_RESTART_CONTAINERS
#       Containers restarted weekly. Aliased by detect_hosts() →
#       WEEKLY_RESTART_CONTAINERS
#
#   HOST*_WATCHDOG_DEPENDENCIES
#       Dependency ordering shared with docker_watchdog.sh. Aliased by
#       detect_hosts() → WATCHDOG_DEPENDENCIES
#
# master.conf
#
#   RETRY_COUNT
#       Retry attempts before giving up on a container
#
#   SLEEP
#       Seconds between retry attempts
#
#   CONTAINER_DELAY
#       Seconds to wait after restarting a dependency before starting its dependents
#
# ==============================================================================================
# RUNTIME MODES
# ==============================================================================================
#
# docker_weekly_restart.sh
#     Restart all containers in WEEKLY_RESTART_CONTAINERS
#
# docker_weekly_restart.sh --dry-run
#     Preview which containers would be restarted and which would be skipped
#
# docker_weekly_restart.sh --status
#     Show configured restart list, container states, and dependency ordering
#
# docker_weekly_restart.sh --log
#     Verbose per-container execution output
#
# ==============================================================================================

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

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

parse_args "$@"

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

if ! command -v docker &>/dev/null; then
    error "Docker command not found — check PATH or Docker installation"
    notify "Docker weekly restart failed — Docker not found on $(hostname)" "Docker Weekly Restart" "warning"
    exit 1
fi

# detect_hosts() sets MY_ID and aliases HOST*_WEEKLY_RESTART_CONTAINERS → WEEKLY_RESTART_CONTAINERS
detect_hosts

if [[ ${#WEEKLY_RESTART_CONTAINERS[@]} -eq 0 ]]; then
    warn "WEEKLY_RESTART_CONTAINERS is empty for $MY_ID — nothing to restart"
    warn "Check HOST*_WEEKLY_RESTART_CONTAINERS in host*.conf"
    exit 0
fi

log "$MY_ID ($LOCAL_SERVER_NAME) — ${#WEEKLY_RESTART_CONTAINERS[@]} containers configured"

# ==============================================================================================
# ━━━ Status ━━━
# ==============================================================================================
if [[ "$SHOW_STATUS" == true ]]; then
    echo ""
    echo "━━━━━ $ICON_SUMMARY STATUS ━━━━━"
    echo "$ICON_HOST       Identity:   $MY_ID ($LOCAL_SERVER_NAME)"
    echo "$ICON_CONTAINERS Containers: ${WEEKLY_RESTART_CONTAINERS[*]}"
    echo "$ICON_RETRY      Retries:    $RETRY_COUNT"
    echo "$ICON_TIME       Sleep:      ${SLEEP}s between retries"
    echo "$ICON_NOTIFY     Notify:     unRAID=${NOTIFY_UNRAID:-false} Discord=$([[ -n "${MY_DISCORD_WEBHOOK:-}" ]] && echo enabled || echo disabled)"
    echo "$ICON_GEAR       Dry Run:    $DRY_RUN"
    echo "━━━━━━━━━━━━━━━━━━━━━━━"
    exit 0
fi

[[ "$DRY_RUN" == true ]] && warn "DRY RUN — no containers will be restarted"

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

# docker_cmd, retry_docker, verify_running — defined in common.sh

# Builds a dependency-safe restart order from WEEKLY_RESTART_CONTAINERS.
# Containers that are dependencies of others restart first.
build_restart_order() {
    ORDERED_RESTART=()
    local remaining=("${WEEKLY_RESTART_CONTAINERS[@]}")
    local placed=()

    # First pass — add dependency containers that appear in our list
    for container in "${remaining[@]}"; do
        [[ -z "$container" ]] && continue
        local is_dependency=false
        for dependent in "${!WATCHDOG_DEPENDENCIES[@]}"; do
            if [[ "${WATCHDOG_DEPENDENCIES[$dependent]}" == *"$container"* ]]; then
                is_dependency=true
                break
            fi
        done
        if [[ "$is_dependency" == true ]]; then
            local already=false
            for p in "${placed[@]}"; do [[ "$p" == "$container" ]] && already=true && break; done
            if [[ "$already" == false ]]; then
                ORDERED_RESTART+=("$container")
                placed+=("$container")
            fi
        fi
    done

    # Second pass — add remaining containers (dependents and independents)
    for container in "${remaining[@]}"; do
        [[ -z "$container" ]] && continue
        local already=false
        for p in "${placed[@]}"; do [[ "$p" == "$container" ]] && already=true && break; done
        if [[ "$already" == false ]]; then
            ORDERED_RESTART+=("$container")
            placed+=("$container")
        fi
    done

    log "Restart order: ${ORDERED_RESTART[*]}"
}

# Waits CONTAINER_DELAY if this container depends on the last restarted one.
check_dependency_delay() {
    local container="$1"
    local last="$2"
    [[ -z "$last" ]] && return
    local deps="${WATCHDOG_DEPENDENCIES[$container]:-}"
    if [[ -n "$deps" ]] && [[ "$deps" == *"$last"* ]]; then
        log "Waiting ${CONTAINER_DELAY}s — $container depends on $last..."
        sleep "$CONTAINER_DELAY"
    fi
}

# ==============================================================================================
# ━━━ Weekly Restart ━━━
# ==============================================================================================
echo ""
echo "━━━ $ICON_CONTAINERS Weekly Restart — $(date '+%Y-%m-%d %H:%M:%S') ━━━"
log "$ICON_CONTAINERS Containers: ${WEEKLY_RESTART_CONTAINERS[*]}"
log "$ICON_RETRY      Retries:    $RETRY_COUNT"
log "$ICON_GEAR       Config:     sleep=${SLEEP}s delay=${CONTAINER_DELAY}s verify-wait=${RESTART_VERIFY_WAIT}s cmd-timeout=${DOCKER_TIMEOUT}s"

START=$(date +%s)
FAILED=()
RESTARTED=()
SKIPPED=()

# Build dependency-safe restart order
build_restart_order

LAST_RESTARTED=""

for container in "${ORDERED_RESTART[@]}"; do
    [[ -z "$container" ]] && continue
    local c_start
    c_start=$(date +%s)
    local c_image
    c_image=$(docker inspect --format '{{.Config.Image}}' "$container" 2>/dev/null || echo "unknown")
    log "━━━ $ICON_CONTAINERS $container ($c_image) ━━━"

    if ! timeout "$DOCKER_TIMEOUT" docker inspect "$container" &>/dev/null; then
        warn "$container does not exist — skipping"
        continue
    fi

    STATUS=$(timeout "$DOCKER_TIMEOUT" docker inspect -f '{{.State.Running}}' "$container" 2>/dev/null)

    case "$STATUS" in
        true)
            log "$ICON_RUNNING $container is running — restarting..."

            # Wait if this container depends on the last one restarted
            check_dependency_delay "$container" "$LAST_RESTARTED"

            if [[ "$DRY_RUN" == true ]]; then
                warn "DRY RUN — would restart $container"
                RESTARTED+=("$container")
            else
                if retry_docker docker restart "$container"; then
                    if verify_running "$container"; then
                        echo "$ICON_STARTED $container restarted and running in $(format_duration $(( $(date +%s) - c_start ))) ✅"
                        RESTARTED+=("$container")
                        LAST_RESTARTED="$container"
                    else
                        error "$container restarted but crashed immediately"
                        notify "$container crashed after restart on $(hostname)" "Docker Weekly Restart" "warning"
                        FAILED+=("$container")
                    fi
                else
                    error "Failed to restart $container after $RETRY_COUNT attempts"
                    notify "$container failed to restart on $(hostname)" "Docker Weekly Restart" "warning"
                    FAILED+=("$container")
                fi
            fi
            ;;
        false)
            # Container was stopped — leave it stopped
            log "$ICON_NOT_RUNNING $container is stopped — skipping (respecting stopped state)"
            SKIPPED+=("$container")
            ;;
        *)
            error "Unknown status for $container: $STATUS"
            FAILED+=("$container")
            ;;
    esac

done

END=$(date +%s)

# ==============================================================================================
# ━━━ Summary ━━━
# ==============================================================================================
echo "━━━━━ $ICON_SUMMARY WEEKLY RESTART SUMMARY ━━━━━"
echo "$ICON_HOST         Identity:  $MY_ID ($LOCAL_SERVER_NAME)"
echo "$ICON_TIME         Duration:  $(format_duration $((END - START)))"
if [[ ${#RESTARTED[@]} -gt 0 ]]; then
    echo "$ICON_STARTED      Restarted: ${#RESTARTED[@]}"
    log "               Names:     ${RESTARTED[*]}"
fi
[[ ${#SKIPPED[@]} -gt 0 ]]   && log "$ICON_NOT_RUNNING  Skipped:   ${SKIPPED[*]} (were stopped)"
[[ ${#FAILED[@]} -gt 0 ]]    && echo "$ICON_ERROR        Failed:    ${FAILED[*]}"

if [[ "$DRY_RUN" == true ]]; then
    echo "$ICON_WARN     Status:    DRY RUN — no changes made"
elif [[ ${#FAILED[@]} -eq 0 ]]; then
    echo "$ICON_DONE     Status:    $ICON_SUCCESS ALL DONE"
    notify "Weekly restart complete — ${#RESTARTED[@]} restarted, ${#SKIPPED[@]} skipped (stopped) on $(hostname)" "Docker Weekly Restart" "normal"
else
    echo "$ICON_ERROR    Status:    $ICON_ERROR ${#FAILED[@]} container(s) failed"
    notify "Weekly restart completed with errors on $(hostname) — failed: ${FAILED[*]}" "Docker Weekly Restart" "warning"
fi
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"

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