#!/usr/bin/env bash

# mb378-R1: complete sample-driven fresh/existing configuration workflow.
# mb381-B1: CPAN permissions and runtime-user verification.

set -Eeuo pipefail
umask 077

APP_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
INSTALL_DIR="$APP_DIR/install"
SAMPLE_CONF="$APP_DIR/mediabot.sample.conf"
CONFIG_HELPER="$INSTALL_DIR/configure_config.pl"
DRIFT_TOOL="$APP_DIR/tools/check_schema_drift.pl"
LOG_FILE="$APP_DIR/configure.log"

CONFIG_FILE=""
LEGACY_SERVER_CONF=""
LEGACY_LIST=0
SYNC_ONLY=0
SKIP_DB=0
SKIP_CPAN=0
DRIFT_ONLY=0
ASSUME_YES=0

usage() {
    cat <<EOF
Mediabot v3 configuration wizard

Usage:
  ./configure [options]

Options:
  -c, --config <file>   Configuration file (default: $APP_DIR/mediabot.conf)
      --sync-only       Synchronize/complete the config and stop
      --skip-db         Skip database creation/drift checks
      --skip-cpan       Skip Perl module installation
      --drift-only      Run database drift workflow only
  -y, --yes             Accept safe default answers where supported
  -s <file>             Legacy server configuration mode
  -l                    List servers in legacy -s mode
  -h, --help            Show this help

Fresh installation:
  - generates a complete config from mediabot.sample.conf;
  - keeps PARTYLINE_EVAL_ENABLED=0;
  - creates the database and application user;
  - installs Perl dependencies;
  - configures IRC/network data;
  - validates schema drift.

Existing installation:
  - creates a timestamped backup;
  - preserves existing values and custom keys;
  - adds missing defaults from mediabot.sample.conf;
  - normalizes duplicate sections/keys atomically;
  - offers schema drift reporting and official migrations;
  - never applies generated SQL automatically.
EOF
}

log() {
    local line="[$(date +'%d/%m/%Y %H:%M:%S')] $*"
    printf '%s\n' "$line" | tee -a "$LOG_FILE"
}

fail() {
    log "ERROR: $*"
    exit 1
}

need_cmd() {
    command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"
}

install_perl_dependencies() {
    # MB393: DBD::MariaDB is a compiled CPAN module used by the runtime DSN.
    # If it is not already installed, require the Connector/C build metadata
    # before any root-side CPAN work starts.
    if ! perl -MDBD::MariaDB -e 'exit 0;' >/dev/null 2>&1; then
        if ! command -v mariadb_config >/dev/null 2>&1 \
            && ! command -v mysql_config >/dev/null 2>&1; then
            fail "DBD::MariaDB must be built by CPAN; install the MariaDB client development files (Debian: libmariadb-dev), not a packaged Perl DBD driver"
        fi
    fi

    log "Installing required Perl modules with CPAN (this may take a while)"
    sudo "$INSTALL_DIR/cpan_install.sh"

    # mb381-B1: the root-side CPAN check cannot detect unreadable site-library
    # paths. Verify the same module set again as the dedicated Mediabot user.
    log "Verifying Perl modules as the Mediabot runtime user"
    "$INSTALL_DIR/cpan_install.sh" --verify-only         || fail "Perl modules were installed but are not readable by the Mediabot runtime user"
}

ask_yes_no() {
    local prompt="$1"
    local default="${2:-y}"
    local answer
    if [[ "$ASSUME_YES" -eq 1 ]]; then
        [[ "$default" == "y" ]]
        return
    fi
    while true; do
        if [[ "$default" == "y" ]]; then
            read -r -p "[$(date +'%d/%m/%Y %H:%M:%S')] $prompt [Y/n]: " answer
            answer="${answer:-y}"
        else
            read -r -p "[$(date +'%d/%m/%Y %H:%M:%S')] $prompt [y/N]: " answer
            answer="${answer:-n}"
        fi
        case "$answer" in
            y|Y|yes|YES) return 0 ;;
            n|N|no|NO) return 1 ;;
        esac
    done
}

absolute_path() {
    local path="$1"
    if [[ "$path" = /* ]]; then
        printf '%s\n' "$path"
    else
        printf '%s/%s\n' "$PWD" "$path"
    fi
}

make_overlay() {
    mktemp /tmp/mediabot_configure_overlay.XXXXXX
}

merge_overlay() {
    local overlay="$1"
    perl "$CONFIG_HELPER" \
        --sample "$SAMPLE_CONF" \
        --config "$CONFIG_FILE" \
        --mode merge \
        --overlay "$overlay" \
        --backup-dir "$CONFIG_DIR/config-backups"
}

get_conf() {
    perl "$CONFIG_HELPER" --config "$CONFIG_FILE" --get "$1"
}

run_drift_workflow() {
    [[ "$SKIP_DB" -eq 0 ]] || return 0
    [[ -f "$CONFIG_FILE" ]] || fail "Cannot check database drift without $CONFIG_FILE"

    log "Running database schema/reference-data drift check"
    set +e
    perl "$DRIFT_TOOL" --conf "$CONFIG_FILE" --strict --types --indexes
    local rc=$?
    set -e

    if [[ "$rc" -eq 0 ]]; then
        log "Database schema is in sync"
        return 0
    fi

    if [[ "$rc" -ne 1 ]]; then
        log "Drift checker could not complete (exit $rc). Check DB credentials and Perl DBI drivers."
        return "$rc"
    fi

    local stamp preview
    stamp="$(date +'%Y%m%d_%H%M%S')"
    preview="$RUN_DIR/schema_drift_${stamp}.txt"
    log "Drift detected. Writing a review-only report to $preview"
    set +e
    perl "$DRIFT_TOOL" --conf "$CONFIG_FILE" --generate-migration --types --indexes >"$preview" 2>&1
    set -e

    printf '\nOfficial migration files (oldest first):\n'
    mapfile -t migrations < <(
        {
            while IFS= read -r migration_file; do
                if [[ "$migration_file" =~ ([0-9]{8}) ]]; then
                    printf '%s\t%s\n' "${BASH_REMATCH[1]}" "$migration_file"
                else
                    printf '99999999\t%s\n' "$migration_file"
                fi
            done < <(
                find "$INSTALL_DIR/migrations" -maxdepth 1 -type f -name '*.sql' -printf '%f\n'
            )
        } | LC_ALL=C sort -k1,1 -k2,2 | cut -f2-
    )
    local i
    for i in "${!migrations[@]}"; do
        printf '  %2d) %s\n' "$((i + 1))" "${migrations[$i]}"
    done
    printf '\nGenerated SQL is never applied automatically. Review the report first.\n'

    if [[ "${#migrations[@]}" -gt 0 ]] && ask_yes_no "Open the guided official-migration selector now?" "n"; then
        local db_name choice selected
        db_name="$(get_conf mysql.MAIN_PROG_DDBNAME)"
        while true; do
            read -r -p "Select migration number (Enter to stop): " choice
            [[ -n "$choice" ]] || break
            [[ "$choice" =~ ^[0-9]+$ ]] || { printf 'Invalid number.\n'; continue; }
            (( choice >= 1 && choice <= ${#migrations[@]} )) || { printf 'Out of range.\n'; continue; }
            selected="${migrations[$((choice - 1))]}"
            printf 'Database : %s\nMigration: %s\n' "$db_name" "$selected"
            if ask_yes_no "Apply this official migration with the root MySQL account?" "n"; then
                sudo "$INSTALL_DIR/db_migrate.sh" "$db_name" "install/migrations/$selected" root
            fi
        done
        log "Re-running drift check after guided migration step"
        perl "$DRIFT_TOOL" --conf "$CONFIG_FILE" --strict --types --indexes
    else
        log "Database drift remains unresolved. Review $preview and install/migrations/ before continuing."
        return 1
    fi
}

while [[ $# -gt 0 ]]; do
    case "$1" in
        -c|--config)
            [[ $# -ge 2 ]] || fail "$1 requires a file"
            CONFIG_FILE="$2"
            shift 2
            ;;
        --sync-only) SYNC_ONLY=1; shift ;;
        --skip-db) SKIP_DB=1; shift ;;
        --skip-cpan) SKIP_CPAN=1; shift ;;
        --drift-only) DRIFT_ONLY=1; shift ;;
        -y|--yes) ASSUME_YES=1; shift ;;
        -s)
            [[ $# -ge 2 ]] || fail "-s requires a configuration file"
            LEGACY_SERVER_CONF="$2"
            shift 2
            ;;
        -l) LEGACY_LIST=1; shift ;;
        -h|--help) usage; exit 0 ;;
        *) fail "Unknown option: $1" ;;
    esac
done

if [[ -n "$LEGACY_SERVER_CONF" ]]; then
    LEGACY_SERVER_CONF="$(absolute_path "$LEGACY_SERVER_CONF")"
    [[ -f "$LEGACY_SERVER_CONF" ]] || fail "$LEGACY_SERVER_CONF does not exist"
    args=(--conf="$LEGACY_SERVER_CONF")
    [[ "$LEGACY_LIST" -eq 1 ]] && args+=(--list)
    exec "$INSTALL_DIR/conf_servers.pl" "${args[@]}"
fi
[[ "$LEGACY_LIST" -eq 0 ]] || fail "-l requires -s <configuration_file>"

[[ "$(id -u)" -ne 0 ]] || fail "Run this wizard as the dedicated non-root Mediabot user, not root"
[[ -f "$SAMPLE_CONF" ]] || fail "Missing $SAMPLE_CONF"
[[ -x "$CONFIG_HELPER" ]] || fail "Missing executable $CONFIG_HELPER"

# mb380-B1: validate the historical CPAN build toolchain before touching DB/config state.
need_cmd perl
need_cmd cpan
need_cmd make
need_cmd gcc
need_cmd mysql
need_cmd sudo
need_cmd mktemp
need_cmd find
if ! command -v curl >/dev/null 2>&1 && ! command -v wget >/dev/null 2>&1; then
    fail "Required download command not found: install curl or wget before running configure"
fi

cd "$APP_DIR"
: >"$LOG_FILE" || fail "Cannot write $LOG_FILE"
chmod 600 "$LOG_FILE"

if [[ -z "$CONFIG_FILE" ]]; then
    default_conf="$APP_DIR/mediabot.conf"
    if [[ "$ASSUME_YES" -eq 1 ]]; then
        CONFIG_FILE="$default_conf"
    else
        read -r -p "[$(date +'%d/%m/%Y %H:%M:%S')] Configuration file [$default_conf]: " CONFIG_FILE
        CONFIG_FILE="${CONFIG_FILE:-$default_conf}"
    fi
fi
CONFIG_FILE="$(absolute_path "$CONFIG_FILE")"
CONFIG_DIR="$(dirname "$CONFIG_FILE")"
RUN_DIR="$CONFIG_DIR/run"
mkdir -p "$RUN_DIR" "$CONFIG_DIR/config-backups"
chmod 750 "$RUN_DIR" "$CONFIG_DIR/config-backups"

if [[ "$DRIFT_ONLY" -eq 1 ]]; then
    [[ -f "$CONFIG_FILE" ]] || fail "Configuration not found: $CONFIG_FILE"
    run_drift_workflow
    exit 0
fi

if [[ -f "$CONFIG_FILE" ]]; then
    INSTALL_MODE="existing"
    log "Existing installation detected: $CONFIG_FILE"
else
    INSTALL_MODE="fresh"
    log "Fresh installation detected: $CONFIG_FILE"
fi

DEFAULTS_FILE="$(make_overlay)"
OVERLAY_FILE="$(make_overlay)"
cleanup() {
    rm -f "${DEFAULTS_FILE:-}" "${OVERLAY_FILE:-}"
}
trap cleanup EXIT
chmod 600 "$DEFAULTS_FILE" "$OVERLAY_FILE"

YTDLP_PATH="$(command -v yt-dlp 2>/dev/null || true)"
YTDLP_PATH="${YTDLP_PATH:-/usr/local/bin/yt-dlp}"
cat >"$DEFAULTS_FILE" <<EOF
main.MAIN_PID_FILE=$CONFIG_DIR/mediabot.pid
main.MAIN_LOG_FILE=$CONFIG_DIR/mediabot.log
main.PARTYLINE_STATUS_JSON=$RUN_DIR/partyline_sessions.json
main.MAIN_PROG_BIRTHDATE=$(date +%s)
radio.YTDLP_PATH=$YTDLP_PATH
EOF

if [[ "$INSTALL_MODE" == "fresh" ]]; then
    command_char='!'
    if [[ "$ASSUME_YES" -eq 0 ]]; then
        while true; do
            read -r -p "[$(date +'%d/%m/%Y %H:%M:%S')] IRC command prefix [!]: " command_char
            command_char="${command_char:-!}"
            [[ "${#command_char}" -eq 1 ]] && [[ "$command_char" != $'\n' ]] && break
            printf 'Enter exactly one character.\n'
        done
    fi
    cat >"$OVERLAY_FILE" <<EOF
main.MAIN_PROG_CMD_CHAR=$command_char
main.PARTYLINE_EVAL_ENABLED=0
EOF
    perl "$CONFIG_HELPER" \
        --sample "$SAMPLE_CONF" \
        --config "$CONFIG_FILE" \
        --mode fresh \
        --defaults "$DEFAULTS_FILE" \
        --overlay "$OVERLAY_FILE"
else
    perl "$CONFIG_HELPER" \
        --sample "$SAMPLE_CONF" \
        --config "$CONFIG_FILE" \
        --mode merge \
        --defaults "$DEFAULTS_FILE" \
        --backup-dir "$CONFIG_DIR/config-backups"
fi

chmod 600 "$CONFIG_FILE"

EVAL_VALUE="$(get_conf main.PARTYLINE_EVAL_ENABLED 2>/dev/null || printf '0')"
if [[ "$EVAL_VALUE" != "0" ]]; then
    log "WARNING: PARTYLINE_EVAL_ENABLED=$EVAL_VALUE is dangerous and is never enabled by this wizard."
    if ask_yes_no "Disable Partyline eval now?" "y"; then
        printf 'main.PARTYLINE_EVAL_ENABLED=0\n' >"$OVERLAY_FILE"
        merge_overlay "$OVERLAY_FILE"
    fi
fi

log "Auditing configuration coverage against mediabot.sample.conf"
perl "$CONFIG_HELPER" --sample "$SAMPLE_CONF" --config "$CONFIG_FILE" --mode audit

if [[ "$SYNC_ONLY" -eq 1 ]]; then
    log "Configuration synchronization completed (--sync-only)"
    exit 0
fi

if [[ "$INSTALL_MODE" == "fresh" ]]; then
    if [[ "$SKIP_DB" -eq 0 ]]; then
        log "Fresh install: creating/configuring the database"
        sudo "$INSTALL_DIR/db_install.sh" -c "$CONFIG_FILE"
    fi

    if [[ "$SKIP_CPAN" -eq 0 ]]; then
        install_perl_dependencies
    fi

    log "Configuring IRC network, server and console channel"
    perl "$INSTALL_DIR/configure.pl" --conf="$CONFIG_FILE"
    run_drift_workflow
else
    if [[ "$SKIP_CPAN" -eq 0 ]] && ask_yes_no "Run the Perl dependency installer/update?" "n"; then
        install_perl_dependencies
    fi

    run_drift_workflow

    if ask_yes_no "Review/update IRC network and console-channel settings?" "n"; then
        perl "$INSTALL_DIR/configure.pl" --conf="$CONFIG_FILE"
    fi
fi

log "Final strict configuration audit"
perl "$CONFIG_HELPER" --sample "$SAMPLE_CONF" --config "$CONFIG_FILE" --mode audit --strict

log "Configuration completed"
log "Config file : $CONFIG_FILE"
log "Backups     : $CONFIG_DIR/config-backups"
log "Runtime dir : $RUN_DIR"
log "Next check  : perl tools/check_schema_drift.pl --conf='$CONFIG_FILE' --strict --types --indexes"
