#!/usr/bin/env bash

set -u
set -o pipefail

if [[ -t 1 ]]; then
    RESET=$'\033[0m'
    RED=$'\033[0;31m'
    GREEN=$'\033[0;32m'
    YELLOW=$'\033[0;33m'
    BRED=$'\033[1;31m'
    BGREEN=$'\033[1;32m'
    BYELLOW=$'\033[1;33m'
    BBLUE=$'\033[1;34m'
else
    RESET=""
    RED=""
    GREEN=""
    YELLOW=""
    BRED=""
    BGREEN=""
    BYELLOW=""
    BBLUE=""
fi

recording_dir="${RECORDING_DIR:-/var/spool/asterisk/monitor}"
test_mode=0
exit_requested=0

declare -i failed_conversion=0
declare -i empty_files=0
declare -i total_wav=0
declare -i total_mp3=0
declare -i total_savings=0
total_savings_percent="0.00"
SCRIPT_NAME="3wmp3"
VERSION="0.2.0"
COPYRIGHT_YEAR="2026"
header_printed=0

usage() {
    cat <<EOF
Usage: ${SCRIPT_NAME} [--test] [--recording-dir PATH] [--version]

Convert FreePBX/Asterisk WAV recordings to MP3 and update the CDR basename.

Options:
  --test                Convert one non-empty WAV file into the current directory
                        without changing the source WAV, database, or ownership.
  --recording-dir PATH  Override the recording directory.
  --version             Show the script version.
  -h, --help            Show this help text.
EOF
}

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

    cat <<EOF
${BBLUE}+------------------------------------------------------------------------------+${RESET}
${BBLUE}|${RESET} ${SCRIPT_NAME} ${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 on non-production recordings before
${BBLUE}|${RESET} production use. 3W Global Corp shall not be liable for any loss, damage,
${BBLUE}|${RESET} data corruption, or service interruption arising from its use.
${BBLUE}+------------------------------------------------------------------------------+${RESET}

EOF

    header_printed=1
}

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

ctrl_c() {
    echo
    exit_requested=1
    echo -e "${BRED}Shutting down MP3 conversion...${RESET}"
}

check_bash_compatibility() {
    if [[ -z "${BASH_VERSION:-}" ]]; then
        echo -e "${BRED}This script must be run with bash.${RESET}" >&2
        exit 1
    fi

    if [[ "${BASH_VERSINFO[0]-0}" -lt 3 ]]; then
        echo -e "${BRED}This script requires bash 3 or newer.${RESET}" >&2
        exit 1
    fi
}

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

require_commands() {
    local -a dependencies=(find sort awk sed lame stat nice)
    local -a missing=()

    if [[ "$test_mode" -eq 0 ]]; then
        dependencies+=(mysql chown rm)
    fi

    for dependency in "${dependencies[@]}"; do
        command_exists "$dependency" || missing+=("$dependency")
    done

    if ((${#missing[@]} == 0)); then
        return 0
    fi

    echo -e "${BRED}Missing required commands:${RESET} ${missing[*]}" >&2
    exit 1
}

human_bytes() {
    local bytes="${1:-0}"
    awk -v bytes="$bytes" '
        function human(x) {
            split("B KiB MiB GiB TiB PiB EiB", units, " ")
            unit = 1
            while (x >= 1024 && unit < length(units)) {
                x /= 1024
                unit++
            }
            if (unit == 1) {
                return sprintf("%d %s", x, units[unit])
            }
            return sprintf("%.2f %s", x, units[unit])
        }
        BEGIN { print human(bytes) }
    '
}

update_totals() {
    local wav_bytes="$1"
    local mp3_bytes="$2"

    total_wav=$((total_wav + wav_bytes))
    total_mp3=$((total_mp3 + mp3_bytes))
    total_savings=$((total_wav - total_mp3))
    total_savings_percent="$(
        awk -v saved="$total_savings" -v wav="$total_wav" 'BEGIN {
            if (wav == 0) {
                print "0.00"
            } else {
                printf "%.2f", (100 * saved) / wav
            }
        }'
    )"
}

print_totals() {
    echo "Total WAV files size $(human_bytes "$total_wav")"
    echo "Total MP3 files size $(human_bytes "$total_mp3")"
    echo "Total size savings $(human_bytes "$total_savings")"
    echo "Total saved ${total_savings_percent}%"
}

sql_escape() {
    printf '%s' "$1" | sed -e "s/\\\\/\\\\\\\\/g" -e "s/'/''/g"
}

update_database_recordingfile() {
    local old_basename="$1"
    local new_basename="$2"
    local escaped_old
    local escaped_new

    escaped_old="$(sql_escape "$old_basename")"
    escaped_new="$(sql_escape "$new_basename")"

    mysql -u root -s -N -D asteriskcdrdb <<SQL
UPDATE cdr
SET recordingfile='${escaped_new}'
WHERE recordingfile='${escaped_old}';
SQL
}

convert_file() {
    local wav_file="$1"
    local wav_basename="${wav_file##*/}"
    local mp3_basename="${wav_basename%.wav}.mp3"
    local mp3_file
    local wav_byte_size
    local wav_size
    local mp3_byte_size
    local mp3_size

    wav_byte_size="$(stat -c '%s' -- "$wav_file")" || {
        echo -e "${BRED}Failed to read size for ${wav_file}${RESET}"
        failed_conversion+=1
        return 1
    }
    wav_size="$(human_bytes "$wav_byte_size")"

    if [[ "$wav_byte_size" -eq 44 || "$wav_byte_size" -eq 0 ]]; then
        empty_files+=1
        echo "$wav_file"
        echo -e "${BRED}Skipping empty WAV file (${wav_byte_size} bytes).${RESET}"
        return 2
    fi

    if [[ "$test_mode" -eq 1 ]]; then
        mp3_file="${PWD}/${mp3_basename}"
        if [[ -e "$mp3_file" ]]; then
            echo -e "${BRED}Test output already exists: ${mp3_file}${RESET}"
            failed_conversion+=1
            return 1
        fi
    else
        mp3_file="${wav_file%.wav}.mp3"
    fi

    echo
    echo -e "${BYELLOW}WAV file size ${wav_size}${RESET}"
    echo "Source: ${wav_file}"

    if ! nice lame -V9.99 -m m "$wav_file" "$mp3_file"; then
        echo -e "${BRED}${wav_file} encoding failed${RESET}"
        failed_conversion+=1
        rm -f -- "$mp3_file" >/dev/null 2>&1 || true
        return 1
    fi

    mp3_byte_size="$(stat -c '%s' -- "$mp3_file")" || {
        echo -e "${BRED}Failed to read MP3 size for ${mp3_file}${RESET}"
        failed_conversion+=1
        return 1
    }
    mp3_size="$(human_bytes "$mp3_byte_size")"
    update_totals "$wav_byte_size" "$mp3_byte_size"

    echo -e "${BGREEN}New MP3 file size ${mp3_size}${RESET}"

    if [[ "$test_mode" -eq 1 ]]; then
        echo -e "${BGREEN}Test conversion complete.${RESET}"
        echo "Output written to ${mp3_file}"
        echo "Original WAV left untouched. Database was not updated."
        echo "-------------------------------------------------------"
        return 0
    fi

    if ! chown asterisk:asterisk -- "$mp3_file"; then
        echo -e "${BRED}Failed to set ownership on ${mp3_file}${RESET}"
        failed_conversion+=1
        return 1
    fi

    if ! update_database_recordingfile "$wav_basename" "$mp3_basename"; then
        echo -e "${BRED}Failed to update database for ${wav_basename}${RESET}"
        failed_conversion+=1
        return 1
    fi

    if ! rm -f -- "$wav_file"; then
        echo -e "${BRED}Failed to remove source WAV ${wav_file}${RESET}"
        failed_conversion+=1
        return 1
    fi

    echo -e "${BGREEN}Resized recording from ${wav_size} WAV to ${mp3_size} MP3 audio${RESET}"
    echo "-------------------------------------------------------"
    return 0
}

main() {
    local found_any=0
    local result=0

    check_bash_compatibility

    while [[ $# -gt 0 ]]; do
        case "$1" in
            --test)
                test_mode=1
                shift
                ;;
            --recording-dir)
                [[ $# -ge 2 ]] || {
                    echo "Missing value for --recording-dir" >&2
                    exit 1
                }
                recording_dir="$2"
                shift 2
                ;;
            --version)
                print_version
                exit 0
                ;;
            -h|--help)
                usage
                exit 0
                ;;
            *)
                echo "Unknown argument: $1" >&2
                usage >&2
                exit 1
                ;;
        esac
    done

    print_header
    require_commands

    if [[ ! -d "$recording_dir" ]]; then
        echo -e "${BRED}Recording directory not found: ${recording_dir}${RESET}"
        exit 1
    fi

    trap ctrl_c SIGINT

    while IFS= read -r -d '' wav_file; do
        found_any=1

        if [[ "$exit_requested" -eq 1 ]]; then
            break
        fi

        convert_file "$wav_file"
        result=$?

        if [[ "$test_mode" -eq 1 && "$result" -ne 2 ]]; then
            break
        fi
    done < <(find "$recording_dir" -type f -name '*.wav' -print0 | sort -z)

    if [[ "$found_any" -eq 0 ]]; then
        echo "No WAV files found in ${recording_dir}."
        exit 0
    fi

    print_totals
    echo
    echo "${failed_conversion} failed to process"
    if [[ "$empty_files" -gt 0 ]]; then
        echo -e "${BRED}Found ${empty_files} empty recording files (0 or 44 bytes).${RESET}"
    fi
}

main "$@"
