#!/bin/bash
#
# Copyright 2023 The qm Authors
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; If not, see <http://www.gnu.org/licenses/>.

if_error_exit() {
    ###########################################################################
    # Description:                                                            #
    # Validate if previous command failed and show an error msg (if provided) #
    #                                                                         #
    # Arguments:                                                              #
    #   $1 - error message if not provided, it will just exit                 #
    ###########################################################################
    local exit_code="$?"
    if [ "${exit_code}" != "0" ]; then
        RED="\033[91m"
        ENDCOLOR="\033[0m"
        echo -e "[ ${RED}FAILED${ENDCOLOR} ] ${1} with exit code: ${exit_code}"
        kill $$ &> /dev/null
    fi
}

info_message() {
    ###########################################################################
    # Description:                                                            #
    # show [INFO] in blue and a message as the validation passed.             #
    #                                                                         #
    # Arguments:                                                              #
    #   $1 - message to output                                                #
    ###########################################################################
    if [ -z "${1}" ]; then
        echo "info_message() requires a message"
        exit 1
    fi
    BLUE="\033[94m"
    ENDCOLOR="\033[0m"
    echo -e "[ ${BLUE}INFO${ENDCOLOR}  ] ${1}"
}

warning_message() {
    ###########################################################################
    # Description:                                                            #
    # show [WARN] in yellow and a message as a warning.                      #
    #                                                                         #
    # Arguments:                                                              #
    #   $1 - message to output                                                #
    ###########################################################################
    if [ -z "${1}" ]; then
        echo "warning_message() requires a message"
        exit 1
    fi
    YELLOW="\033[93m"
    ENDCOLOR="\033[0m"
    echo -e "[ ${YELLOW}WARN${ENDCOLOR}  ] ${1}"
}

pass_message() {
    ###########################################################################
    # Description:                                                            #
    # show [PASS] in green and a message as the test passed.                  #
    #                                                                         #
    # Arguments:                                                              #
    #   $1 - message to output                                                #
    ###########################################################################
    if [ -z "${1}" ]; then
        echo "pass_message() requires a message"
        exit 1
    fi
    GREEN="\033[92m"
    ENDCOLOR="\033[0m"
    echo -e "[ ${GREEN}PASS${ENDCOLOR}  ] ${1}"
}

fail_message() {
    ###########################################################################
    # Description:                                                            #
    # show [FAIL] in red and a message as the test failed.                    #
    #                                                                         #
    # Arguments:                                                              #
    #   $1 - message to output                                                #
    ###########################################################################
    if [ -z "${1}" ]; then
        echo "fail_message() requires a message"
        exit 1
    fi
    RED="\033[91m"
    ENDCOLOR="\033[0m"
    echo -e "[ ${RED}FAIL${ENDCOLOR}  ] ${1}"
}

# Validation functions for run_test
validate_output_pattern() {
    local test_name="$1"
    local validation_value="$2"
    local stdout_file="$3"

    if [ -n "$validation_value" ] && grep -q "$validation_value" "$stdout_file"; then
        pass_message "$test_name - Output contains expected pattern"
    else
        fail_message "$test_name - Output doesn't contain expected pattern '$validation_value'"
        echo "Actual output (first 10 lines):"
        head -10 < "$stdout_file"
        exit 1
    fi
}

validate_error_pattern() {
    local test_name="$1"
    local validation_value="$2"
    local stderr_file="$3"

    if [ -z "$stderr_file" ]; then
        fail_message "$test_name - No error message captured"
        exit 1
    elif grep -q "$validation_value" "$stderr_file"; then
        pass_message "$test_name - Error message contains expected pattern"
    else
        fail_message "$test_name - Error message doesn't contain expected pattern '$validation_value'"
        echo "Actual stderr:"
        cat "$stderr_file"
        exit 1
    fi
}

validate_json_valid() {
    local test_name="$1"
    local validation_value="$2"
    local stdout_file="$3"

    if python3 -m json.tool < "$stdout_file" > /dev/null 2>&1; then
        pass_message "$test_name - Valid JSON output"
        if grep -q '{' "$stdout_file" && grep -q '}' "$stdout_file"; then
            pass_message "$test_name - JSON contains expected braces"
        fi
        if grep -q "$validation_value" "$stdout_file"; then
            pass_message "$test_name - JSON contains expected content '$validation_value'"
        else
            fail_message "$test_name - JSON doesn't contain expected content '$validation_value'"
            echo "Actual JSON output:"
            cat "$stdout_file"
            exit 1
        fi
    else
        fail_message "$test_name - Invalid JSON output"
        echo "Output:"
        head -10 < "$stdout_file"
        exit 1
    fi
}

validate_none() {
    # Only exit code check, no additional validation
    :
}

run_test() {
    ###########################################################################
    # Description:                                                            #
    # General test function that runs commands and validates results          #
    #                                                                         #
    # Arguments:                                                              #
    #   $1 - test_name: Name of the test for output                          #
    #   $2 - expected_exit_code: Expected exit code (0, 1, 2, etc.)         #
    #   $3 - validation_type: Type of validation to perform                   #
    #       "output_pattern" - Check for pattern in stdout                    #
    #       "error_pattern" - Check for pattern in stderr                     #
    #       "json_valid" - Validate JSON structure and content                #
    #       "content_match" - Check if output contains data                   #
    #       "file_exists" - Check if file exists and has content              #
    #       "none" - Only check exit code                                     #
    #   $4 - validation_value: Pattern, content, or file path to validate    #
    #   $@ - command: Command and arguments to execute                        #
    ###########################################################################
    local test_name="$1"
    local expected_exit_code="$2"
    local validation_type="$3"
    local validation_value="$4"
    shift 4
    local command=("$@")

    info_message "Testing: $test_name"

    # Create temp files for output
    local stdout_file
    local stderr_file
    stdout_file=$(mktemp)
    stderr_file=$(mktemp)

    # Run command and capture exit code
    if "${command[@]}" > "$stdout_file" 2> "$stderr_file"; then
        local actual_exit_code=0
    else
        local actual_exit_code=$?
    fi

    # Check exit code
    if [ "$actual_exit_code" -eq "$expected_exit_code" ]; then
        pass_message "$test_name - Exit code correct ($expected_exit_code)"
    else
        fail_message "$test_name - Expected exit code $expected_exit_code, got $actual_exit_code"
        echo "  Command: ${command[*]}"
        echo "  stdout:"
        head -5 < "$stdout_file"
        echo "  stderr:"
        head -5 < "$stderr_file"
        rm -f "$stdout_file" "$stderr_file"
        exit 1
    fi

    # Perform validation based on type
    case "$validation_type" in
        "output_pattern")
            validate_output_pattern "$test_name" "$validation_value" "$stdout_file"
            ;;
        "error_pattern")
            validate_error_pattern "$test_name" "$validation_value" "$stderr_file"
            ;;
        "json_valid")
            validate_json_valid "$test_name" "$validation_value" "$stdout_file"
            ;;
        "none")
            validate_none
            ;;
        *)
            fail_message "$test_name - Unknown validation type: $validation_type"
            exit 1
            ;;
    esac

    # Cleanup
    rm -f "$stdout_file" "$stderr_file"
    return 0
}

exec_cmd() {
    local cmd_string="$1"

    # Use bash -c to interpret the string as a command
    bash -c "$cmd_string"
    if_error_exit "Error executing command '$cmd_string'"
    info_message "PASS: Command '$cmd_string' successful"
}

cleanup_node_services() {
    bluechi_nodes=$(bluechictl status | grep online | cut -d'|' -f 1)
    for node in ${bluechi_nodes}; do
       containers_list=$(bluechictl list-units "{node}" | grep container- | cut -d"|" -f2)
       for srv in ${containers_list}; do
           bluechictl stop "${node} ${srv}"
           bluechictl disable "${node} ${srv}"
       done
       if grep -q "qm" <<< "${node}"; then
           rm -rf /etc/qm/containers/systemd/*
           podman exec -t qm bash -c "systemctl daemon-reload"
           podman exec -t qm bash -c "systemctl reset-failed"
           podman exec -t qm bash -c \
 "podman rmi -f $(podman images --quiet  --filter reference=ubi9*)"
        else
           rm -rf /etc/containers/systemd/*
           systemctl daemon-reload
           systemctl reset-failed
           podman rmi -f "$(podman images --quiet  --filter reference=ubi9*)"
        fi
    done
}

cleanup() {
    info_message "Cleaning any existing artifacts that were generated during previous runs"
    output=$(podman ps -a --format "{{.Names}}")
    IFS=$'\n' # Set the internal field separator to newline

    # DO NOT use double quotes here
    for node in ${output}; do
        if [[ "${node}" == "control" ]] || [[ "${node}" =~ ^node.* ]]; then
            info_message "  - Removing container: ${node}"
            # Make sure we remove previous settings
            podman rm --storage --force "${node}" &> /dev/null
            if_error_exit "error removing storage ${1}"

            podman rm "${node}" --force 1> /dev/null
            if_error_exit "error removing container ${1}"

            rm -f ./*.node*
        fi
    done
    info_message "  - Removing podmanDualStack network..."
    info_message " "
    podman network rm podmanDualStack -f 1> /dev/null

}

# Utility function to create configuration files
create_config_file() {
    local file_path="$1"
    local content="$2"
    local description="$3"

    if [[ -n "$description" ]]; then
        info_message "Creating $description..."
    fi

    # Create directory if it doesn't exist
    local dir_path
    dir_path=$(dirname "$file_path")
    mkdir -p "$dir_path"

    # Create the file with content
    printf "%s\n" "$content" > "$file_path"
    if [[ -n "$description" ]]; then
        info_message "$description created successfully at $file_path"
    fi
}

# Utility function to append content to existing files
append_to_config_file() {
    local file_path="$1"
    local content="$2"
    local description="$3"

    if [[ ! -f "$file_path" ]]; then
        warning_message "Configuration file not found: $file_path"
        return 1
    fi

    # Check if content already exists to avoid duplicates
    if grep -Fq "$content" "$file_path"; then
        info_message "$description already exists in $file_path"
        return 0
    fi

    if [[ -n "$description" ]]; then
        info_message "Appending $description to $file_path..."
    fi

    echo "$content" >> "$file_path"

    if [[ -n "$description" ]]; then
        info_message "$description appended successfully"
    fi
}

# Utility function to display section headers
section_header() {
    local title="$1"
    local separator="${2:-==============================}"

    info_message "$title"
    info_message "$separator"
}
