#!/usr/bin/env bash

set -euo pipefail

SCRIPT_DIR=""
TARGET_DIR="/usr/local/sbin"
BASE_URL="${THREEWMP3_BASE_URL:-}"
TMP_FILE=""
PREPARE_SYSTEM_ONLY=0
ROLLBACK_LAST=0
REINSTALL_SAME_VERSION=0
ASSUME_YES=0
MISSING_RUNTIME_COMMANDS=()
REQUIRED_PACKAGES=()
SCRIPT_NAME="3wmp3_install.sh"
RUNTIME_NAME="3wmp3"
RELEASE_VERSION="0.2.0"
COPYRIGHT_YEAR="2026"
BACKUP_DIR_NAME=".3wmp3-backups"
header_printed=0

if [[ -t 1 ]]; then
    RESET=$'\033[0m'
    BRed=$'\033[1;31m'
    BGreen=$'\033[1;32m'
    BYellow=$'\033[1;33m'
    BBlue=$'\033[1;34m'
else
    RESET=""
    BRed=""
    BGreen=""
    BYellow=""
    BBlue=""
fi

usage() {
    cat <<EOF
Usage: ${SCRIPT_NAME} [--base-url URL] [--target-dir PATH] [--prepare-system] [--rollback-last] [--reinstall] [--yes] [--version]

Install:
  - ${RUNTIME_NAME}

Behavior:
  - if \`--base-url\` is set, download ${RUNTIME_NAME} from that URL
  - otherwise, install the local ./${RUNTIME_NAME} file next to this installer
  - install missing client-side runtime dependencies for ${RUNTIME_NAME}
  - detect an existing install, compare versions, and offer an upgrade
  - back up the existing install before replacing it
  - \`--rollback-last\` restores the latest backup in ${TARGET_DIR}/${BACKUP_DIR_NAME}
  - \`--reinstall\` forces the selected release to be installed again even if the same version is already present
  - \`--prepare-system\` installs dependencies only and skips the ${RUNTIME_NAME} install
EOF
}

print_info() {
    printf '%sINFO%s %s\n' "${BBlue}" "${RESET}" "$1"
}

print_ok() {
    printf '%sOK%s %s\n' "${BGreen}" "${RESET}" "$1"
}

print_warn() {
    printf '%sWARN%s %s\n' "${BYellow}" "${RESET}" "$1"
}

print_error() {
    printf '%sERROR%s %s\n' "${BRed}" "${RESET}" "$1" >&2
}

fail() {
    print_error "$*"
    exit 1
}

cleanup() {
    if [[ -n "${TMP_FILE}" && -f "${TMP_FILE}" ]]; then
        rm -f "${TMP_FILE}"
    fi
}

trap cleanup EXIT

print_header() {
    if [[ "${THREEW_INSTALLER_SUPPRESS_HEADER:-0}" == "1" ]]; then
        header_printed=1
        return 0
    fi

    if [[ "${header_printed}" -eq 1 ]]; then
        return 0
    fi

    cat <<EOF
${BBlue}+------------------------------------------------------------------------------+${RESET}
${BBlue}|${RESET} ${SCRIPT_NAME} ${RELEASE_VERSION}
${BBlue}|${RESET} Copyright (c) ${COPYRIGHT_YEAR} 3W Global Corp. All rights reserved.
${BBlue}|${RESET} ${BYellow}NOTICE:${RESET} This software is provided "as is", without warranties or
${BBlue}|${RESET} guarantees of any kind. Use it at your own risk. You are solely responsible
${BBlue}|${RESET} for backups, validation, and testing before production use. 3W Global Corp
${BBlue}|${RESET} shall not be liable for any loss, damage, data corruption, or service
${BBlue}|${RESET} interruption arising from its installation or use.
${BBlue}+------------------------------------------------------------------------------+${RESET}

EOF

    header_printed=1
}

print_version() {
    printf '%s %s\n' "${SCRIPT_NAME}" "${RELEASE_VERSION}"
}

check_bash_compatibility() {
    if [[ -z "${BASH_VERSION:-}" ]]; then
        fail "this installer must be run with bash"
    fi

    if [[ "${BASH_VERSINFO[0]-0}" -lt 3 ]]; then
        fail "this installer requires bash 3 or newer"
    fi
}

init_script_dir() {
    local script_source=""

    if [[ -n "${BASH_SOURCE[0]-}" ]]; then
        script_source="${BASH_SOURCE[0]}"
    elif [[ -n "${0:-}" && "${0}" != "bash" && "${0}" != "-bash" ]]; then
        script_source="${0}"
    fi

    if [[ -n "${script_source}" && -e "${script_source}" ]]; then
        SCRIPT_DIR="$(cd "$(dirname "${script_source}")" && pwd)"
    fi
}

command_exists() {
    command -v "$1" >/dev/null 2>&1
}

confirm_terms() {
    if [[ "${THREEW_INSTALLER_ASSUME_TERMS:-0}" == "1" ]]; then
        return 0
    fi

    if [[ "${ASSUME_YES}" -eq 1 ]]; then
        print_info "Proceeding with non-interactive acceptance of the license notice and terms."
        return 0
    fi

    local reply=""

    if [[ -r /dev/tty ]]; then
        printf 'Proceed and confirm that you accept the license notice and terms above? [y/N] ' > /dev/tty
        IFS= read -r reply < /dev/tty || reply=""
    elif [[ -t 0 ]]; then
        printf 'Proceed and confirm that you accept the license notice and terms above? [y/N] '
        IFS= read -r reply || reply=""
    fi

    case "${reply}" in
        y|Y|yes|YES)
            print_ok "License notice and terms accepted."
            return 0
            ;;
        *)
            print_warn "Cancelled. License notice and terms were not accepted."
            exit 1
            ;;
    esac
}

is_root() {
    [[ "$(id -u)" -eq 0 ]]
}

append_unique_package() {
    local value="$1"
    local existing=""

    for existing in "${REQUIRED_PACKAGES[@]:-}"; do
        if [[ "${existing}" == "${value}" ]]; then
            return 0
        fi
    done

    REQUIRED_PACKAGES+=("${value}")
}

collect_missing_runtime_commands() {
    local dependency=""
    local required_commands="find sort awk sed lame stat nice mysql chown rm"

    MISSING_RUNTIME_COMMANDS=()

    for dependency in ${required_commands}; do
        command_exists "${dependency}" || MISSING_RUNTIME_COMMANDS+=("${dependency}")
    done
}

package_manager_name() {
    if command_exists apt-get; then
        echo "apt-get"
        return 0
    fi
    if command_exists dnf; then
        echo "dnf"
        return 0
    fi
    if command_exists yum; then
        echo "yum"
        return 0
    fi
    if command_exists zypper; then
        echo "zypper"
        return 0
    fi
    return 1
}

map_commands_to_packages() {
    local manager="$1"
    local dependency=""

    REQUIRED_PACKAGES=()

    for dependency in "${MISSING_RUNTIME_COMMANDS[@]}"; do
        case "${dependency}" in
            find)
                append_unique_package "findutils"
                ;;
            sort|stat|nice|chown|rm)
                append_unique_package "coreutils"
                ;;
            awk)
                append_unique_package "gawk"
                ;;
            sed)
                append_unique_package "sed"
                ;;
            lame)
                append_unique_package "lame"
                ;;
            mysql)
                case "${manager}" in
                    apt-get)
                        append_unique_package "default-mysql-client"
                        ;;
                    dnf|yum)
                        append_unique_package "mariadb"
                        ;;
                    zypper)
                        append_unique_package "mariadb-client"
                        ;;
                esac
                ;;
        esac
    done
}

print_dependency_install_command() {
    printf '%q ' "$@"
    echo
}

install_runtime_dependencies() {
    local manager=""
    local install_cmd=""
    local -a install_argv=()

    collect_missing_runtime_commands

    if ((${#MISSING_RUNTIME_COMMANDS[@]} == 0)); then
        print_ok "All runtime dependencies currently look available."
        return 0
    fi

    print_info "This helper expects an existing PBX system and database. It does not install a database server."
    print_warn "Missing runtime commands: ${MISSING_RUNTIME_COMMANDS[*]}"

    manager="$(package_manager_name)" || {
        print_warn "No supported package manager was detected."
        print_warn "Install the required client-side runtime packages manually, then run the installer again."
        return 1
    }

    map_commands_to_packages "${manager}"
    install_argv=("${manager}" "install" "-y" "${REQUIRED_PACKAGES[@]}")
    install_cmd="$(print_dependency_install_command "${install_argv[@]}")"

    if is_root; then
        print_info "Installing required client-side packages:"
        echo "  ${install_cmd}"
        "${install_argv[@]}"
    elif command_exists sudo; then
        print_info "Installing required client-side packages with sudo:"
        echo "  sudo ${install_cmd}"
        sudo "${install_argv[@]}"
    else
        print_warn "Run this as root to install them:"
        echo "  ${install_cmd}"
        return 1
    fi

    if [[ "${manager}" == "dnf" || "${manager}" == "yum" ]]; then
        print_info "RPM note: the \`mariadb\` package above is the client package that provides \`mysql\`, not \`mariadb-server\`."
    fi
}

download_source() {
    local source_url="${BASE_URL%/}/${RUNTIME_NAME}"

    TMP_FILE="$(mktemp)"
    if command_exists curl; then
        curl -fsSL "${source_url}" -o "${TMP_FILE}"
    elif command_exists wget; then
        wget -qO "${TMP_FILE}" "${source_url}"
    else
        fail "neither curl nor wget is available to download ${source_url}"
    fi
    printf '%s\n' "${TMP_FILE}"
}

resolve_source() {
    local local_source=""

    if [[ -n "${BASE_URL}" ]]; then
        download_source
        return 0
    fi

    if [[ -n "${SCRIPT_DIR}" ]]; then
        local_source="${SCRIPT_DIR}/${RUNTIME_NAME}"
        if [[ -f "${local_source}" ]]; then
            printf '%s\n' "${local_source}"
            return 0
        fi
    fi

    fail "no local ${RUNTIME_NAME} was found and no --base-url was provided"
}

extract_version_from_file() {
    local target_file="$1"
    local version=""

    if [[ -x "${target_file}" ]]; then
        version="$("${target_file}" --version 2>/dev/null | awk 'NR == 1 { print $2; exit }')"
    fi

    if [[ -z "${version}" ]]; then
        version="$(awk -F'"' '/^VERSION="/ { print $2; exit }' "${target_file}" 2>/dev/null)"
    fi

    if [[ -z "${version}" ]]; then
        version="unknown"
    fi

    printf '%s\n' "${version}"
}

prompt_yes_no() {
    local prompt="$1"
    local reply=""

    if [[ "${ASSUME_YES}" -eq 1 ]]; then
        return 0
    fi

    if [[ -r /dev/tty ]]; then
        printf '%s' "${prompt}" > /dev/tty
        IFS= read -r reply < /dev/tty || return 1
    elif [[ -t 0 ]]; then
        printf '%s' "${prompt}"
        IFS= read -r reply || return 1
    else
        return 1
    fi

    case "${reply}" in
        ""|y|Y|yes|YES)
            return 0
            ;;
        *)
            return 1
            ;;
    esac
}

backup_existing_install() {
    local target_file="$1"
    local installed_version="$2"
    local backup_root="${TARGET_DIR}/${BACKUP_DIR_NAME}"
    local version_slug=""
    local stamp=""
    local backup_dir=""

    version_slug="$(printf '%s' "${installed_version}" | sed 's/[^A-Za-z0-9._-]/_/g')"
    stamp="$(date '+%Y%m%d-%H%M%S')"
    backup_dir="${backup_root}/${stamp}-${version_slug}"

    mkdir -p "${backup_dir}"
    cp -p "${target_file}" "${backup_dir}/${RUNTIME_NAME}"
    printf '%s\n' "${backup_dir}/${RUNTIME_NAME}"
}

latest_backup_file() {
    local backup_root="${TARGET_DIR}/${BACKUP_DIR_NAME}"

    if [[ ! -d "${backup_root}" ]]; then
        return 1
    fi

    find "${backup_root}" -mindepth 2 -maxdepth 2 -type f -name "${RUNTIME_NAME}" | sort | tail -n 1
}

print_test_notice() {
    local installed_path="${TARGET_DIR}/${RUNTIME_NAME}"

    cat <<EOF
Run tests before production use:
  ${installed_path} --version
  ${installed_path} --help
  ${installed_path} --test --recording-dir /path/to/sample-recordings

Review the generated MP3 output and validate database behavior before using this on
production recordings.
EOF
}

rollback_last_install() {
    local backup_file=""
    local target_file="${TARGET_DIR}/${RUNTIME_NAME}"

    backup_file="$(latest_backup_file)" || fail "no backup version is available under ${TARGET_DIR}/${BACKUP_DIR_NAME}"
    mkdir -p "${TARGET_DIR}"
    install -m 0755 "${backup_file}" "${target_file}"
    echo "Restored ${RUNTIME_NAME} from backup: ${backup_file}"
    print_test_notice
}

perform_install() {
    local source_script="$1"
    local target_file="${TARGET_DIR}/${RUNTIME_NAME}"
    local installed_version=""
    local backup_file=""
    local reinstalled_same_version=0

    if [[ -f "${target_file}" ]]; then
        installed_version="$(extract_version_from_file "${target_file}")"

        if [[ "${installed_version}" == "${RELEASE_VERSION}" ]]; then
            if [[ "${REINSTALL_SAME_VERSION}" -eq 0 ]]; then
                print_warn "${RUNTIME_NAME} ${RELEASE_VERSION} is already installed at ${target_file}."
                if prompt_yes_no "Reinstall ${RELEASE_VERSION} and back up the current install? [y/N] "; then
                    REINSTALL_SAME_VERSION=1
                else
                    print_info "No reinstall is required."
                    print_test_notice
                    return 0
                fi
            fi

            backup_file="$(backup_existing_install "${target_file}" "${installed_version}")"
            reinstalled_same_version=1
            print_ok "Backed up the current install to: ${backup_file}"
        else
            print_warn "Detected installed ${RUNTIME_NAME} version: ${installed_version}"
            print_info "Available release version: ${RELEASE_VERSION}"

            if ! prompt_yes_no "Upgrade to ${RELEASE_VERSION} and back up the current install? [Y/n] "; then
                print_warn "Upgrade cancelled."
                exit 0
            fi

            backup_file="$(backup_existing_install "${target_file}" "${installed_version}")"
            print_ok "Backed up the previous install to: ${backup_file}"
        fi
    fi

    mkdir -p "${TARGET_DIR}"
    install -m 0755 "${source_script}" "${target_file}"

    if [[ "${reinstalled_same_version}" -eq 1 ]]; then
        print_ok "Reinstalled ${target_file}"
    else
        print_ok "Installed ${target_file}"
    fi
    if [[ -n "${backup_file}" ]]; then
        print_info "Rollback command:"
        echo "  ${SCRIPT_NAME} --target-dir ${TARGET_DIR} --rollback-last"
    fi
    print_test_notice
}

init_script_dir
check_bash_compatibility

while [[ $# -gt 0 ]]; do
    case "$1" in
        --base-url)
            [[ $# -ge 2 ]] || fail "--base-url requires a value"
            BASE_URL="$2"
            shift 2
            ;;
        --target-dir)
            [[ $# -ge 2 ]] || fail "--target-dir requires a value"
            TARGET_DIR="$2"
            shift 2
            ;;
        --prepare-system)
            PREPARE_SYSTEM_ONLY=1
            shift
            ;;
        --rollback-last)
            ROLLBACK_LAST=1
            shift
            ;;
        --reinstall|--force-install|--clean-install)
            REINSTALL_SAME_VERSION=1
            shift
            ;;
        --yes)
            ASSUME_YES=1
            shift
            ;;
        --version)
            print_version
            exit 0
            ;;
        -h|--help)
            usage
            exit 0
            ;;
        *)
            fail "unexpected argument: $1"
            ;;
    esac
done

print_header
confirm_terms

if [[ "${ROLLBACK_LAST}" -eq 1 ]]; then
    rollback_last_install
    exit 0
fi

install_runtime_dependencies

if [[ "${PREPARE_SYSTEM_ONLY}" -eq 1 ]]; then
    print_ok "System preparation complete. ${RUNTIME_NAME} was not installed."
    print_info "When you install it, run tests before production use."
    exit 0
fi

SOURCE_SCRIPT="$(resolve_source)"
[[ -f "${SOURCE_SCRIPT}" ]] || fail "missing source script at ${SOURCE_SCRIPT}"

perform_install "${SOURCE_SCRIPT}"
