#!/usr/bin/env bash
# audit_doctor_snapshot — ejecuta un subcomando de fn doctor, guarda snapshot JSON
# fechado, compara con la corrida anterior y emite resumen legible de cambios.
#
# Uso: audit_doctor_snapshot <doctor_subcommand> <snapshot_base_dir>
#
# Ejemplo:
#   audit_doctor_snapshot unused /home/enmanuel/fn_registry/apps/dag_engine/local_files/audits/daily

set -uo pipefail

audit_doctor_snapshot() {
    local sub="${1:-}"
    local base="${2:-}"

    # --- validacion de argumentos ---
    if [[ -z "$sub" || -z "$base" ]]; then
        echo "usage: audit_doctor_snapshot <subcommand> <base_dir>" >&2
        return 2
    fi

    # --- resolver binario fn ---
    local fn_bin="${FN_BIN:-${FN_REGISTRY_ROOT:-$HOME/fn_registry}/fn}"
    if [[ ! -x "$fn_bin" ]]; then
        echo "audit_doctor_snapshot: binario fn no encontrado o no ejecutable: $fn_bin" >&2
        return 2
    fi

    # --- preparar directorio ---
    local dir="$base/$sub"
    mkdir -p "$dir"

    # --- ejecutar fn doctor ---
    local stderr_tmp
    stderr_tmp="$(mktemp /tmp/audit_doctor_snapshot_stderr.XXXXXX)"
    local json rc
    json="$("$fn_bin" doctor "$sub" --json 2>"$stderr_tmp")" || rc=$?
    rc="${rc:-0}"

    if [[ "$rc" -ne 0 ]]; then
        cat "$stderr_tmp" >&2
        echo "audit_doctor_snapshot: 'fn doctor $sub' fallo (rc=$rc)" >&2
        rm -f "$stderr_tmp"
        return "$rc"
    fi
    rm -f "$stderr_tmp"

    # --- normalizar con jq (diff estable) ---
    local stamp
    stamp="$(date -u +%Y%m%dT%H%M%SZ)"
    local curr="$dir/${stamp}.json"
    local nojson=0

    if ! echo "$json" | jq -S . > "$curr" 2>/dev/null; then
        # salida no es JSON valido -> guardar crudo
        printf '%s' "$json" > "$curr"
        nojson=1
    fi

    # --- snapshot anterior ---
    local prev="$dir/latest.json"

    # --- contar hallazgos actuales ---
    local count="?"
    if [[ "$nojson" -eq 0 ]]; then
        if jq -e 'type == "array"' "$curr" >/dev/null 2>&1; then
            count="$(jq 'length' "$curr")"
        elif jq -e 'type == "object"' "$curr" >/dev/null 2>&1; then
            count="$(jq 'keys | length' "$curr")"
        fi
    fi

    # --- contar hallazgos previos ---
    local prevcount="-"
    if [[ -f "$prev" ]]; then
        if jq -e 'type == "array"' "$prev" >/dev/null 2>&1; then
            prevcount="$(jq 'length' "$prev")"
        elif jq -e 'type == "object"' "$prev" >/dev/null 2>&1; then
            prevcount="$(jq 'keys | length' "$prev")"
        fi
    fi

    # --- diff de identidad ---
    local new_count=0
    local resolved_count=0
    local new_ids=()
    local resolved_ids=()
    local diff_label=""

    if [[ ! -f "$prev" ]]; then
        diff_label="baseline (sin corrida previa)"
    elif [[ "$nojson" -eq 1 ]]; then
        if ! diff -q "$prev" "$curr" >/dev/null 2>&1; then
            diff_label="changed (textual)"
        else
            diff_label="+0 new  -0 resolved"
        fi
    else
        # extraer IDs estables: .ID o .id
        local curr_ids prev_ids
        curr_ids="$(jq -r 'if type=="array" then .[].ID // .[].id // empty else to_entries[].value.ID // to_entries[].value.id // empty end' "$curr" 2>/dev/null | sort -u)"
        prev_ids="$(jq -r 'if type=="array" then .[].ID // .[].id // empty else to_entries[].value.ID // to_entries[].value.id // empty end' "$prev" 2>/dev/null | sort -u)"

        if [[ -n "$curr_ids" || -n "$prev_ids" ]]; then
            # NEW: en curr pero no en prev
            local new_raw resolved_raw
            new_raw="$(comm -23 <(echo "$curr_ids") <(echo "$prev_ids") 2>/dev/null || true)"
            resolved_raw="$(comm -13 <(echo "$curr_ids") <(echo "$prev_ids") 2>/dev/null || true)"

            if [[ -n "$new_raw" ]]; then
                mapfile -t new_ids <<< "$new_raw"
            fi
            if [[ -n "$resolved_raw" ]]; then
                mapfile -t resolved_ids <<< "$resolved_raw"
            fi

            new_count="${#new_ids[@]}"
            resolved_count="${#resolved_ids[@]}"
            diff_label="+${new_count} new  -${resolved_count} resolved"
        else
            # sin campo .ID/.id — fallback textual
            if ! diff -q "$prev" "$curr" >/dev/null 2>&1; then
                diff_label="changed (textual)"
            else
                diff_label="+0 new  -0 resolved"
            fi
        fi
    fi

    # --- resumen a stdout ---
    echo "[audit:$sub] count=$count prev=$prevcount  $diff_label"

    # listar nuevos (max 8)
    if [[ "${#new_ids[@]}" -gt 0 ]]; then
        local listed=("${new_ids[@]:0:8}")
        local extra=$(( ${#new_ids[@]} - 8 ))
        local line
        line="$(IFS=', '; echo "${listed[*]}")"
        if [[ "$extra" -gt 0 ]]; then
            line="${line} (+${extra} más)"
        fi
        echo "  NEW: $line"
    fi

    # listar resueltos (max 8)
    if [[ "${#resolved_ids[@]}" -gt 0 ]]; then
        local listed_r=("${resolved_ids[@]:0:8}")
        local extra_r=$(( ${#resolved_ids[@]} - 8 ))
        local line_r
        line_r="$(IFS=', '; echo "${listed_r[*]}")"
        if [[ "$extra_r" -gt 0 ]]; then
            line_r="${line_r} (+${extra_r} más)"
        fi
        echo "  RESOLVED: $line_r"
    fi

    # --- actualizar puntero latest ---
    cp "$curr" "$prev"

    # --- retención: borrar snapshots fechados > 30 días ---
    find "$dir" -maxdepth 1 -name '*.json' ! -name 'latest.json' -mtime +30 -delete 2>/dev/null || true

    return 0
}

# Permitir ejecución directa
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    audit_doctor_snapshot "$@"
fi
