#!/usr/bin/env bash
# run_steps — ejecuta pasos de un YAML generico (action=command)

# run_steps <yaml_file> [--strict]
#
# Lee un YAML con la estructura:
#   name: <run_name>
#   steps:
#     - name: <step_name>
#       action: command
#       command: "<shell_command>"
#       continue_on_error: true|false   # opcional, default false
#       timeout_ms: 30000               # opcional, default 30000
#
# Para cada paso de action=command:
#   - Ejecuta el command con timeout (timeout_ms ms)
#   - Captura exit code, stdout+stderr y elapsed time
#   - Si falla y continue_on_error=false → aborta
#   - Acumula resultados en TSV temporal
#
# Al final:
#   - Llama a report_execution_json para generar JSON a stdout
#   - Llama a exit_with_status para determinar el exit code
#
# --strict: mapea partial (2) a failure (1) en status y exit code
#
# Requiere: yq, jq (jq opcional si report_execution_json tiene fallback printf)
run_steps() {
    local yaml_file="$1"
    local strict=0

    if [[ "${2:-}" == "--strict" ]]; then
        strict=1
    fi

    # --- validaciones previas ---
    if [[ -z "$yaml_file" ]]; then
        echo "run_steps: yaml_file requerido" >&2
        return 1
    fi

    if [[ ! -f "$yaml_file" ]]; then
        echo "run_steps: archivo '$yaml_file' no existe" >&2
        return 1
    fi

    if ! command -v yq &>/dev/null; then
        echo "run_steps: yq no encontrado en PATH" >&2
        return 1
    fi

    # --- cargar funciones del estandar ---
    local script_dir
    script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
    # shellcheck source=bash/functions/shell/exit_with_status.sh
    source "$script_dir/exit_with_status.sh"
    # shellcheck source=bash/functions/shell/report_execution_json.sh
    source "$script_dir/report_execution_json.sh"

    # --- leer nombre del run ---
    local run_name
    run_name=$(yq e '.name // "unnamed"' "$yaml_file")

    # --- contar pasos ---
    local step_count
    step_count=$(yq e '.steps | length' "$yaml_file")

    if [[ -z "$step_count" || "$step_count" -eq 0 ]]; then
        echo "run_steps: el YAML no tiene steps" >&2
        return 1
    fi

    # --- archivo TSV temporal para acumular resultados ---
    # columnas: name<TAB>action<TAB>status<TAB>elapsed_ms<TAB>output<TAB>error
    local tsv_file
    tsv_file=$(mktemp /tmp/run_steps_XXXXXX.tsv)
    # shellcheck disable=SC2064
    trap "rm -f '$tsv_file'" EXIT

    local ok_steps=0
    local failed_steps=0
    local started_at
    started_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
    local t_run_start
    t_run_start=$(date +%s%3N)

    # --- iterar sobre cada paso ---
    local i
    for (( i=0; i<step_count; i++ )); do
        local step_name step_action step_command step_continue step_timeout_ms

        step_name=$(yq e ".steps[$i].name // \"step_$i\"" "$yaml_file")
        step_action=$(yq e ".steps[$i].action // \"command\"" "$yaml_file")
        step_command=$(yq e ".steps[$i].command // \"\"" "$yaml_file")
        step_continue=$(yq e ".steps[$i].continue_on_error // false" "$yaml_file")
        step_timeout_ms=$(yq e ".steps[$i].timeout_ms // 30000" "$yaml_file")

        # convertir timeout de ms a segundos enteros (timeout(1) usa segundos)
        local timeout_sec
        timeout_sec=$(( (step_timeout_ms + 999) / 1000 ))

        # solo soportamos action=command
        if [[ "$step_action" != "command" ]]; then
            local err_msg="action '$step_action' no soportada (solo command)"
            printf '%s\t%s\t%s\t%s\t%s\t%s\n' \
                "$step_name" "$step_action" "error" "0" "" "$err_msg" >> "$tsv_file"
            failed_steps=$((failed_steps + 1))

            if [[ "$step_continue" != "true" ]]; then
                echo "run_steps: paso '$step_name' — $err_msg — abortando" >&2
                break
            fi
            continue
        fi

        if [[ -z "$step_command" ]]; then
            local err_msg="campo 'command' vacio en paso '$step_name'"
            printf '%s\t%s\t%s\t%s\t%s\t%s\n' \
                "$step_name" "command" "error" "0" "" "$err_msg" >> "$tsv_file"
            failed_steps=$((failed_steps + 1))

            if [[ "$step_continue" != "true" ]]; then
                echo "run_steps: $err_msg — abortando" >&2
                break
            fi
            continue
        fi

        # ejecutar con timeout y capturar output + tiempos
        local t_start t_end elapsed_ms step_output step_exit

        t_start=$(date +%s%3N)
        step_output=$(timeout "$timeout_sec" bash -c "$step_command" 2>&1)
        step_exit=$?
        t_end=$(date +%s%3N)
        elapsed_ms=$(( t_end - t_start ))

        # timeout(1) sale con 124 cuando agota el tiempo
        local step_error=""
        if [[ "$step_exit" -eq 124 ]]; then
            step_error="timeout: comando excedio ${step_timeout_ms}ms"
        fi

        # escapar tabuladores y saltos de linea en el output para el TSV
        local safe_output
        safe_output=$(printf '%s' "$step_output" | tr '\t' ' ' | tr '\n' ' ')

        if [[ "$step_exit" -eq 0 ]]; then
            printf '%s\t%s\t%s\t%s\t%s\t%s\n' \
                "$step_name" "command" "ok" "$elapsed_ms" "$safe_output" "" >> "$tsv_file"
            ok_steps=$((ok_steps + 1))
        else
            printf '%s\t%s\t%s\t%s\t%s\t%s\n' \
                "$step_name" "command" "error" "$elapsed_ms" "$safe_output" "$step_error" >> "$tsv_file"
            failed_steps=$((failed_steps + 1))

            if [[ "$step_continue" != "true" ]]; then
                echo "run_steps: paso '$step_name' fallo (exit $step_exit) — abortando" >&2
                break
            fi
        fi
    done

    local t_run_end
    t_run_end=$(date +%s%3N)
    local total_duration_ms=$(( t_run_end - t_run_start ))
    local ended_at
    ended_at=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
    local total_steps=$(( ok_steps + failed_steps ))

    # --- calcular status y exit code ---
    local status_code
    status_code=$(exit_with_status "$total_steps" "$ok_steps" "$failed_steps")

    local run_status
    case "$status_code" in
        0) run_status="success" ;;
        1) run_status="failure" ;;
        2)
            if [[ "$strict" -eq 1 ]]; then
                run_status="failure"
                status_code=1
            else
                run_status="partial"
            fi
            ;;
        *) run_status="failure"; status_code=1 ;;
    esac

    # --- generar JSON a stdout ---
    report_execution_json \
        "$run_name" \
        "$run_status" \
        "$status_code" \
        "$started_at" \
        "$ended_at" \
        "$total_duration_ms" \
        "$tsv_file"

    return "$status_code"
}
