#!/bin/bash
################################################################################
# Script Name: restic-backup-exporter.sh
# Version: 1.0
# Description: Prometheus exporter for restic backups — last snapshot time,
#              backup age, repo health, snapshot counts, and size metrics
#
# Author: Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
#
# Prerequisites:
#   - restic installed
#   - RESTIC_PASSWORD_FILE or RESTIC_PASSWORD env set
#   - netcat (nc) for HTTP mode
#
# Usage:
#   ./restic-backup-exporter.sh --repo /mnt/backup --textfile
#   ./restic-backup-exporter.sh --repo /mnt/backup --http -p 9200
#   RESTIC_REPOSITORY=/mnt/backup ./restic-backup-exporter.sh
#
# Configuration:
#   Default HTTP port: 9200
#   Textfile directory: /var/lib/node_exporter
#
################################################################################

TEXTFILE_DIR="/var/lib/node_exporter"
OUTPUT_FILE=""
HTTP_MODE=false
HTTP_PORT=9200
REPOS=()

show_usage() {
    cat <<EOF
Usage: $0 [OPTIONS]

Export restic backup statistics as Prometheus metrics.

MODES:
    --textfile      Write to node_exporter textfile collector
    --http          Run HTTP server on port $HTTP_PORT

OPTIONS:
    -p, --port      HTTP port (default: 9200)
    -o, --output    Output file path
    --repo          Restic repository path (repeatable)

EXAMPLES:
    $0 --repo /mnt/backup --textfile
    $0 --repo /mnt/backup1 --repo /mnt/backup2 --http
    RESTIC_REPOSITORY=/mnt/backup $0 --textfile

EOF
    exit 0
}

parse_args() {
    while [[ $# -gt 0 ]]; do
        case $1 in
            -h|--help) show_usage ;;
            --textfile) OUTPUT_FILE="$TEXTFILE_DIR/restic_backup.prom"; shift ;;
            --http) HTTP_MODE=true; shift ;;
            -p|--port) HTTP_PORT="$2"; shift 2 ;;
            -o|--output) OUTPUT_FILE="$2"; shift 2 ;;
            --repo) REPOS+=("$2"); shift 2 ;;
            *) echo "Unknown option: $1" >&2; exit 1 ;;
        esac
    done
    if [ ${#REPOS[@]} -eq 0 ] && [ -n "$RESTIC_REPOSITORY" ]; then
        REPOS+=("$RESTIC_REPOSITORY")
    fi
}

check_restic() {
    if ! command -v restic >/dev/null 2>&1; then
        echo "ERROR: restic not found" >&2
        return 1
    fi
    return 0
}

generate_repo_metrics() {
    local repo="$1"
    local repo_label
    repo_label=$(echo "$repo" | sed 's/[^a-zA-Z0-9_\/-]/_/g')

    # Check repo exists
    if ! restic -r "$repo" cat config >/dev/null 2>&1; then
        echo "restic_backup_repo_initialized{repo=\"$repo_label\"} 0"
        return
    fi
    echo "restic_backup_repo_initialized{repo=\"$repo_label\"} 1"

    # Snapshots
    local snapshots_json
    snapshots_json=$(restic -r "$repo" snapshots --json 2>/dev/null)
    local snapshot_count
    snapshot_count=$(echo "$snapshots_json" | jq 'length')

    echo "restic_backup_snapshot_count_total{repo=\"$repo_label\"} ${snapshot_count:-0}"

    # Last snapshot
    local last_ts
    last_ts=$(echo "$snapshots_json" | jq -r 'sort_by(.time) | last | .time // empty' 2>/dev/null)
    if [ -n "$last_ts" ]; then
        local last_unix
        last_unix=$(date -d "$last_ts" +%s 2>/dev/null || echo 0)
        local now
        now=$(date +%s)
        local age=$((now - last_unix))
        echo "restic_backup_last_snapshot_timestamp{repo=\"$repo_label\"} $last_unix"
        echo "restic_backup_seconds_since_last_snapshot{repo=\"$repo_label\"} $age"
    else
        echo "restic_backup_last_snapshot_timestamp{repo=\"$repo_label\"} 0"
        echo "restic_backup_seconds_since_last_snapshot{repo=\"$repo_label\"} 0"
    fi

    # Per-host counts
    echo "$snapshots_json" | jq -r --arg repo "$repo_label" '
        group_by(.hostname) | .[] |
        "restic_backup_snapshot_count_by_host{repo=\"\($repo)\",hostname=\"\(.[0].hostname)\"} \(length)"
    ' 2>/dev/null

    # Stats
    local stats_json
    stats_json=$(restic -r "$repo" stats --json 2>/dev/null)
    local total_size file_count
    total_size=$(echo "$stats_json" | jq '.total_size // 0')
    file_count=$(echo "$stats_json" | jq '.total_file_count // 0')
    echo "restic_backup_size_bytes_total{repo=\"$repo_label\"} ${total_size:-0}"
    echo "restic_backup_file_count_total{repo=\"$repo_label\"} ${file_count:-0}"

    # Latest snapshot size
    local latest_size
    latest_size=$(echo "$snapshots_json" | jq 'sort_by(.time) | last | .summary.total_bytes_processed // 0' 2>/dev/null)
    echo "restic_backup_latest_snapshot_size_bytes{repo=\"$repo_label\"} ${latest_size:-0}"

    # Repo health
    if restic -r "$repo" check --read-data-subset=1% >/dev/null 2>&1; then
        echo "restic_backup_repo_healthy{repo=\"$repo_label\"} 1"
    else
        echo "restic_backup_repo_healthy{repo=\"$repo_label\"} 0"
    fi

    # Lock status
    local locks
    locks=$(restic -r "$repo" list locks 2>/dev/null | wc -l)
    if [ "$locks" -gt 0 ]; then
        echo "restic_backup_repo_locked{repo=\"$repo_label\"} 1"
    else
        echo "restic_backup_repo_locked{repo=\"$repo_label\"} 0"
    fi
}

generate_metrics() {
    local script_start
    script_start=$(date +%s)

    if ! check_restic; then
        echo "# HELP restic_backup_repo_healthy Repo health status"
        echo "# TYPE restic_backup_repo_healthy gauge"
        echo "restic_backup_repo_healthy 0"
        return
    fi

    cat <<EOF
# HELP restic_backup_repo_initialized Repo initialized status
# TYPE restic_backup_repo_initialized gauge
# HELP restic_backup_snapshot_count_total Total snapshot count
# TYPE restic_backup_snapshot_count_total gauge
# HELP restic_backup_last_snapshot_timestamp Last snapshot unix timestamp
# TYPE restic_backup_last_snapshot_timestamp gauge
# HELP restic_backup_seconds_since_last_snapshot Seconds since last snapshot
# TYPE restic_backup_seconds_since_last_snapshot gauge
# HELP restic_backup_snapshot_count_by_host Snapshots per hostname
# TYPE restic_backup_snapshot_count_by_host gauge
# HELP restic_backup_size_bytes_total Total backup size in bytes
# TYPE restic_backup_size_bytes_total gauge
# HELP restic_backup_file_count_total Total file count
# TYPE restic_backup_file_count_total gauge
# HELP restic_backup_latest_snapshot_size_bytes Latest snapshot size
# TYPE restic_backup_latest_snapshot_size_bytes gauge
# HELP restic_backup_repo_healthy Repo health from restic check
# TYPE restic_backup_repo_healthy gauge
# HELP restic_backup_repo_locked Repo lock status
# TYPE restic_backup_repo_locked gauge
EOF

    for repo in "${REPOS[@]}"; do
        generate_repo_metrics "$repo"
    done

    echo ""
    local script_end script_duration
    script_end=$(date +%s)
    script_duration=$((script_end - script_start))

    cat <<EOF
# HELP restic_backup_exporter_duration_seconds Script execution time
# TYPE restic_backup_exporter_duration_seconds gauge
restic_backup_exporter_duration_seconds $script_duration

# HELP restic_backup_exporter_last_run_timestamp Last successful run
# TYPE restic_backup_exporter_last_run_timestamp gauge
restic_backup_exporter_last_run_timestamp $script_end
EOF
}

run_http_server() {
    echo "Starting restic backup exporter on port $HTTP_PORT..." >&2
    if ! command -v nc >/dev/null 2>&1; then
        echo "ERROR: netcat (nc) required for HTTP mode" >&2
        exit 1
    fi
    while true; do
        {
            read -r request
            if [[ "$request" =~ ^GET\ /metrics ]]; then
                echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/plain; version=0.0.4\r\n\r"
                generate_metrics
            else
                echo -e "HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r"
                echo "<html><head><title>Restic Backup Exporter</title></head><body><h1>Restic Backup Prometheus Exporter</h1><p><a href=\"/metrics\">Metrics</a></p></body></html>"
            fi
        } | nc -l -p "$HTTP_PORT" -q 1 2>/dev/null
    done
}

main() {
    parse_args "$@"
    if [ "$HTTP_MODE" = true ]; then
        run_http_server
    elif [ -n "$OUTPUT_FILE" ]; then
        local output_dir
        output_dir="$(dirname "$OUTPUT_FILE")"
        mkdir -p "$output_dir"
        local temp_file
        temp_file=$(mktemp "${output_dir}/.restic_metrics.XXXXXX")
        if ! generate_metrics > "$temp_file" 2>/dev/null; then
            rm -f "$temp_file"
            echo "ERROR: Failed to generate metrics" >&2
            exit 1
        fi
        chmod 644 "$temp_file"
        mv -f "$temp_file" "$OUTPUT_FILE"
        echo "Metrics written to $OUTPUT_FILE" >&2
    else
        generate_metrics
    fi
}

main "$@"
