#!/usr/bin/env bash
#
# Alloy Config Generator
#
# Interactive script that generates a Grafana Alloy configuration
# file based on your environment. Asks what backends you use, what
# signals to collect, and what services to monitor, then outputs
# a working config.alloy ready to deploy.
#
# Usage:
#   ./alloy-config-generator.sh
#   ./alloy-config-generator.sh -o /etc/alloy/config.alloy
#   ./alloy-config-generator.sh --non-interactive --metrics --logs --prometheus-url http://mimir:9009
#
# Parameters:
#   -o, --output FILE           Write config to file (default: stdout)
#   --non-interactive           Skip prompts, use flags and defaults
#   --metrics                   Enable host metrics collection
#   --logs                      Enable log collection
#   --traces                    Enable OTLP trace collection
#   --journald                  Enable journald log collection
#   --docker                    Enable Docker container log collection
#   --nginx                     Enable nginx log collection
#   --prometheus-url URL        Prometheus/Mimir remote_write URL
#   --loki-url URL              Loki push URL
#   --tempo-url URL             Tempo OTLP endpoint (host:port)
#   --hostname NAME             Hostname label for metrics and logs
#   --scrape-targets LIST       Comma-separated host:port targets to scrape
#   --help                      Show usage
#
# Author:  Phil Connor
# Contact: contact@mylinux.work
# Website: https://mylinux.work
# License: MIT
# Version: 1.0

set -euo pipefail

# --- Configuration ---
readonly VERSION="1.0"
readonly SCRIPT_NAME="$(basename "$0")"

# Defaults
OUTPUT=""
NON_INTERACTIVE=false
ENABLE_METRICS=false
ENABLE_LOGS=false
ENABLE_TRACES=false
ENABLE_JOURNALD=false
ENABLE_DOCKER=false
ENABLE_NGINX=false
PROMETHEUS_URL=""
LOKI_URL=""
TEMPO_URL=""
HOSTNAME=""
SCRAPE_TARGETS=""

# --- Functions ---

usage() {
    cat <<EOF
Usage: $SCRIPT_NAME [OPTIONS]

Alloy Config Generator v${VERSION}

Generates a Grafana Alloy configuration file interactively or from flags.

Options:
  -o, --output FILE           Write config to file (default: stdout)
  --non-interactive           Skip prompts, use flags and defaults
  --metrics                   Enable host metrics collection
  --logs                      Enable log collection
  --traces                    Enable OTLP trace collection
  --journald                  Enable journald log collection
  --docker                    Enable Docker container log collection
  --nginx                     Enable nginx log collection
  --prometheus-url URL        Prometheus/Mimir remote_write URL
  --loki-url URL              Loki push URL
  --tempo-url URL             Tempo OTLP endpoint (host:port)
  --hostname NAME             Hostname label
  --scrape-targets LIST       Comma-separated host:port targets to scrape
  --help                      Show this help

Examples:
  $SCRIPT_NAME
  $SCRIPT_NAME -o /etc/alloy/config.alloy
  $SCRIPT_NAME --non-interactive --metrics --logs --prometheus-url http://mimir:9009/api/v1/push --loki-url http://loki:3100/loki/api/v1/push
EOF
    exit 0
}

ask() {
    local prompt="$1"
    local default="$2"
    local var_name="$3"
    local response

    if [[ "$NON_INTERACTIVE" == true ]]; then
        eval "$var_name=\"$default\""
        return
    fi

    if [[ -n "$default" ]]; then
        read -r -p "$prompt [$default]: " response
        eval "$var_name=\"${response:-$default}\""
    else
        read -r -p "$prompt: " response
        eval "$var_name=\"$response\""
    fi
}

ask_yn() {
    local prompt="$1"
    local default="$2"
    local var_name="$3"

    if [[ "$NON_INTERACTIVE" == true ]]; then
        return
    fi

    local yn_hint="y/n"
    [[ "$default" == "y" ]] && yn_hint="Y/n"
    [[ "$default" == "n" ]] && yn_hint="y/N"

    local response
    read -r -p "$prompt ($yn_hint): " response
    response="${response:-$default}"

    case "$response" in
        [Yy]*) eval "$var_name=true" ;;
        *) eval "$var_name=false" ;;
    esac
}

interactive_setup() {
    echo ""
    echo "Alloy Config Generator v${VERSION}"
    echo "================================="
    echo ""
    echo "This generates a Grafana Alloy config.alloy file."
    echo ""

    # Hostname
    local detected_hostname
    detected_hostname=$(hostname -s 2>/dev/null || echo "server")
    ask "Hostname label" "$detected_hostname" HOSTNAME
    echo ""

    # --- Backends ---
    echo "== Backends =="
    echo ""

    ask "Prometheus/Mimir remote_write URL (leave empty to skip metrics)" "http://prometheus:9090/api/v1/write" PROMETHEUS_URL
    if [[ -n "$PROMETHEUS_URL" ]]; then
        ENABLE_METRICS=true
    fi

    ask "Loki push URL (leave empty to skip logs)" "http://loki:3100/loki/api/v1/push" LOKI_URL
    if [[ -n "$LOKI_URL" ]]; then
        ENABLE_LOGS=true
    fi

    ask "Tempo OTLP endpoint host:port (leave empty to skip traces)" "" TEMPO_URL
    if [[ -n "$TEMPO_URL" ]]; then
        ENABLE_TRACES=true
    fi

    echo ""

    # --- Metrics options ---
    if [[ "$ENABLE_METRICS" == true ]]; then
        echo "== Metrics =="
        echo ""

        local extra_targets=""
        ask "Additional Prometheus scrape targets (comma-separated host:port, or empty)" "" extra_targets
        if [[ -n "$extra_targets" ]]; then
            SCRAPE_TARGETS="$extra_targets"
        fi
        echo ""
    fi

    # --- Log options ---
    if [[ "$ENABLE_LOGS" == true ]]; then
        echo "== Logs =="
        echo ""
        ask_yn "Collect journald logs?" "y" ENABLE_JOURNALD
        ask_yn "Collect Docker container logs?" "n" ENABLE_DOCKER
        ask_yn "Collect nginx logs?" "n" ENABLE_NGINX
        echo ""
    fi
}

# --- Config generation functions ---

generate_header() {
    cat <<EOF
// Grafana Alloy Configuration
// Generated by alloy-config-generator.sh v${VERSION}
// $(date +"%Y-%m-%d %H:%M:%S")
//
// Hostname: ${HOSTNAME}
// Docs: https://mylinux.work/guides/grafana-alloy-setup/

EOF
}

generate_metrics() {
    if [[ "$ENABLE_METRICS" != true ]]; then
        return
    fi

    cat <<EOF
// =========================================
// Metrics: host metrics → ${PROMETHEUS_URL}
// =========================================

prometheus.exporter.unix "default" {
  set_collectors = ["cpu", "diskstats", "filesystem", "loadavg", "meminfo", "netdev", "uname"]
  filesystem {
    mount_points_exclude = "^/(sys|proc|dev|run)(\$|/)"
  }
}

prometheus.scrape "node" {
  targets         = prometheus.exporter.unix.default.targets
  scrape_interval = "15s"
  forward_to      = [prometheus.remote_write.default.receiver]
}

// Scrape Alloy internal metrics
prometheus.scrape "alloy_self" {
  targets = [
    {"__address__" = "localhost:12345"},
  ]
  scrape_interval = "30s"
  forward_to      = [prometheus.remote_write.default.receiver]
}

EOF

    # Additional scrape targets
    if [[ -n "$SCRAPE_TARGETS" ]]; then
        cat <<EOF
// Additional scrape targets
prometheus.scrape "extra" {
  targets = [
EOF
        IFS=',' read -ra targets <<< "$SCRAPE_TARGETS"
        for target in "${targets[@]}"; do
            target=$(echo "$target" | xargs)  # trim whitespace
            echo "    {\"__address__\" = \"${target}\"},"
        done
        cat <<EOF
  ]
  scrape_interval = "15s"
  forward_to      = [prometheus.remote_write.default.receiver]
}

EOF
    fi

    cat <<EOF
prometheus.remote_write "default" {
  endpoint {
    url = "${PROMETHEUS_URL}"
  }
  external_labels = {
    host = "${HOSTNAME}",
  }
}

EOF
}

generate_logs_header() {
    if [[ "$ENABLE_LOGS" != true ]]; then
        return
    fi

    cat <<EOF
// =========================================
// Logs → ${LOKI_URL}
// =========================================

EOF
}

generate_logs_system() {
    if [[ "$ENABLE_LOGS" != true ]]; then
        return
    fi

    # Detect OS family for log paths
    local syslog_path="/var/log/syslog"
    local auth_path="/var/log/auth.log"
    if [[ -f /etc/redhat-release ]] || [[ -f /etc/rocky-release ]] || [[ -f /etc/almalinux-release ]]; then
        syslog_path="/var/log/messages"
        auth_path="/var/log/secure"
    fi

    cat <<EOF
// System log files
local.file_match "system_logs" {
  path_targets = [
    {__path__ = "${syslog_path}", job = "syslog"},
    {__path__ = "${auth_path}",   job = "auth"},
  ]
}

loki.source.file "system" {
  targets    = local.file_match.system_logs.targets
  forward_to = [loki.process.add_host.receiver]
}

loki.process "add_host" {
  forward_to = [loki.write.default.receiver]

  stage.static_labels {
    values = {
      host = "${HOSTNAME}",
    }
  }
}

EOF
}

generate_logs_journald() {
    if [[ "$ENABLE_JOURNALD" != true ]]; then
        return
    fi

    cat <<EOF
// Journald
loki.source.journal "default" {
  max_age       = "12h"
  relabel_rules = loki.relabel.journal.rules
  forward_to    = [loki.write.default.receiver]
}

loki.relabel "journal" {
  forward_to = []
  rule {
    source_labels = ["__journal__systemd_unit"]
    target_label  = "unit"
  }
  rule {
    source_labels = ["__journal__hostname"]
    target_label  = "host"
  }
  rule {
    source_labels = ["__journal__priority_keyword"]
    target_label  = "level"
  }
}

EOF
}

generate_logs_docker() {
    if [[ "$ENABLE_DOCKER" != true ]]; then
        return
    fi

    cat <<EOF
// Docker container logs
discovery.docker "containers" {
  host = "unix:///var/run/docker.sock"
}

discovery.relabel "docker" {
  targets = discovery.docker.containers.targets

  rule {
    source_labels = ["__meta_docker_container_name"]
    target_label  = "container"
    regex         = "/(.*)"
  }
  rule {
    source_labels = ["__meta_docker_container_log_stream"]
    target_label  = "stream"
  }
}

loki.source.docker "default" {
  host       = "unix:///var/run/docker.sock"
  targets    = discovery.docker.containers.targets
  labels     = {"host" = "${HOSTNAME}", "job" = "docker"}
  forward_to = [loki.write.default.receiver]
  relabel_rules = discovery.relabel.docker.rules
}

EOF
}

generate_logs_nginx() {
    if [[ "$ENABLE_NGINX" != true ]]; then
        return
    fi

    cat <<EOF
// Nginx logs
local.file_match "nginx" {
  path_targets = [
    {__path__ = "/var/log/nginx/access.log", job = "nginx_access"},
    {__path__ = "/var/log/nginx/error.log",  job = "nginx_error"},
  ]
}

loki.source.file "nginx" {
  targets    = local.file_match.nginx.targets
  forward_to = [loki.process.nginx.receiver]
}

loki.process "nginx" {
  forward_to = [loki.write.default.receiver]

  stage.static_labels {
    values = {
      host = "${HOSTNAME}",
    }
  }

  stage.regex {
    expression = "^(?P<remote_addr>\\\\S+) - (?P<remote_user>\\\\S+) \\\\[(?P<timestamp>.+?)\\\\] \"(?P<method>\\\\S+) (?P<path>\\\\S+) (?P<protocol>\\\\S+)\" (?P<status>\\\\d+) (?P<bytes>\\\\d+)"
  }

  stage.labels {
    values = {
      method = "",
      status = "",
    }
  }
}

EOF
}

generate_logs_write() {
    if [[ "$ENABLE_LOGS" != true ]]; then
        return
    fi

    cat <<EOF
loki.write "default" {
  endpoint {
    url = "${LOKI_URL}"
  }
}

EOF
}

generate_traces() {
    if [[ "$ENABLE_TRACES" != true ]]; then
        return
    fi

    cat <<EOF
// =========================================
// Traces: OTLP → ${TEMPO_URL}
// =========================================

otelcol.receiver.otlp "default" {
  grpc {
    endpoint = "0.0.0.0:4317"
  }
  http {
    endpoint = "0.0.0.0:4318"
  }
  output {
    traces = [otelcol.processor.batch.default.input]
  }
}

otelcol.processor.batch "default" {
  timeout = "5s"
  output {
    traces = [otelcol.exporter.otlp.tempo.input]
  }
}

otelcol.exporter.otlp "tempo" {
  client {
    endpoint = "${TEMPO_URL}"
    tls {
      insecure = true
    }
  }
}

EOF
}

generate_config() {
    generate_header
    generate_metrics
    generate_logs_header
    generate_logs_system
    generate_logs_journald
    generate_logs_docker
    generate_logs_nginx
    generate_logs_write
    generate_traces
}

# --- Main ---

main() {
    # Parse arguments
    while [[ $# -gt 0 ]]; do
        case "$1" in
            -o|--output) OUTPUT="$2"; shift 2 ;;
            --non-interactive) NON_INTERACTIVE=true; shift ;;
            --metrics) ENABLE_METRICS=true; shift ;;
            --logs) ENABLE_LOGS=true; shift ;;
            --traces) ENABLE_TRACES=true; shift ;;
            --journald) ENABLE_JOURNALD=true; shift ;;
            --docker) ENABLE_DOCKER=true; shift ;;
            --nginx) ENABLE_NGINX=true; shift ;;
            --prometheus-url) PROMETHEUS_URL="$2"; ENABLE_METRICS=true; shift 2 ;;
            --loki-url) LOKI_URL="$2"; ENABLE_LOGS=true; shift 2 ;;
            --tempo-url) TEMPO_URL="$2"; ENABLE_TRACES=true; shift 2 ;;
            --hostname) HOSTNAME="$2"; shift 2 ;;
            --scrape-targets) SCRAPE_TARGETS="$2"; shift 2 ;;
            --help|-h) usage ;;
            *) echo "Unknown option: $1" >&2; usage ;;
        esac
    done

    # Set hostname default
    if [[ -z "$HOSTNAME" ]]; then
        HOSTNAME=$(hostname -s 2>/dev/null || echo "server")
    fi

    # Set backend URL defaults for non-interactive mode
    if [[ "$NON_INTERACTIVE" == true ]]; then
        [[ "$ENABLE_METRICS" == true && -z "$PROMETHEUS_URL" ]] && PROMETHEUS_URL="http://prometheus:9090/api/v1/write"
        [[ "$ENABLE_LOGS" == true && -z "$LOKI_URL" ]] && LOKI_URL="http://loki:3100/loki/api/v1/push"
        [[ "$ENABLE_TRACES" == true && -z "$TEMPO_URL" ]] && TEMPO_URL="tempo:4317"
    fi

    # Interactive mode
    if [[ "$NON_INTERACTIVE" != true ]]; then
        interactive_setup
    fi

    # Check at least one signal is enabled
    if [[ "$ENABLE_METRICS" != true && "$ENABLE_LOGS" != true && "$ENABLE_TRACES" != true ]]; then
        echo "ERROR: No signals enabled. Enable at least one of: metrics, logs, traces" >&2
        exit 1
    fi

    # Generate config
    if [[ -n "$OUTPUT" ]]; then
        generate_config > "$OUTPUT"
        echo ""
        echo "Config written to: $OUTPUT"
        echo ""
        echo "Signals enabled:"
        [[ "$ENABLE_METRICS" == true ]]  && echo "  ✓ Metrics  → $PROMETHEUS_URL"
        [[ "$ENABLE_LOGS" == true ]]     && echo "  ✓ Logs     → $LOKI_URL"
        [[ "$ENABLE_TRACES" == true ]]   && echo "  ✓ Traces   → $TEMPO_URL"
        echo ""
        echo "Next steps:"
        echo "  1. Review the config: cat $OUTPUT"
        echo "  2. Validate syntax:   alloy fmt $OUTPUT"
        echo "  3. Test it:           alloy run $OUTPUT"
        echo "  4. Deploy:            sudo cp $OUTPUT /etc/alloy/config.alloy && sudo systemctl restart alloy"
    else
        generate_config
    fi
}

main "$@"
