#!/usr/bin/env bash
# inspect_ssl_cert
# ----------------
# Inspecciona el certificado SSL/TLS de un host: sujeto, emisor, fechas de validez,
# SANs, cadena de confianza y versiones de TLS aceptadas (detecta TLS 1.0/1.1 inseguros).
#
# USO (directo):
#   inspect_ssl_cert <host[:puerto]>
#   inspect_ssl_cert example.com
#   inspect_ssl_cert example.com:8443
#
# Depende de: openssl, timeout

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/../shell/bash_colors.sh"
source "$SCRIPT_DIR/../shell/bash_log.sh"
bash_colors
bash_log_init

# ─── Constantes ───────────────────────────────────────────────────────────────

_SSL_WARN_DAYS=30

# ─── Funciones puras ──────────────────────────────────────────────────────────

_ssl_parse_host_port() {
    local input="$1"
    if [[ "$input" =~ ^(.+):([0-9]+)$ ]]; then
        echo "${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"
    else
        echo "${input} 443"
    fi
}

_ssl_days_until_expiry() {
    local expiry_str="$1"
    local expiry_epoch
    expiry_epoch="$(date -d "$expiry_str" +%s 2>/dev/null || echo 0)"
    local now_epoch
    now_epoch="$(date +%s)"
    echo $(( (expiry_epoch - now_epoch) / 86400 ))
}

# ─── Funciones de efecto ──────────────────────────────────────────────────────

_ssl_fetch_subject() {
    local host="$1"
    local port="$2"
    echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
        | openssl x509 -noout -subject -issuer 2>/dev/null
}

_ssl_fetch_dates() {
    local host="$1"
    local port="$2"
    echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
        | openssl x509 -noout -dates 2>/dev/null
}

_ssl_fetch_san() {
    local host="$1"
    local port="$2"
    echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" 2>/dev/null \
        | openssl x509 -noout -ext subjectAltName 2>/dev/null \
        | grep -oE 'DNS:[^,]+' | sed 's/DNS://g' | tr '\n' ' '
}

_ssl_fetch_chain() {
    local host="$1"
    local port="$2"
    echo | timeout 10 openssl s_client -connect "${host}:${port}" -servername "$host" \
        -showcerts 2>/dev/null \
        | grep -E "^(subject|issuer)=" | sed 's/^/    /'
}

_ssl_check_tls_version() {
    local host="$1"
    local port="$2"
    local proto="$3"
    local label="$4"
    if echo | timeout 5 openssl s_client -connect "${host}:${port}" \
        -servername "$host" "${proto}" 2>/dev/null | grep -q "Cipher"; then
        echo -e "  ${RED}[x]${NC} ${label} -- soportado (inseguro)"
    else
        echo -e "  ${GREEN}[ok]${NC} ${label} no soportado"
    fi
}

# ─── Punto de entrada ─────────────────────────────────────────────────────────

inspect_ssl_cert() {
    local input="$1"

    if [[ -z "$input" ]]; then
        error "inspect_ssl_cert: se requiere un host como argumento (ej: example.com o example.com:8443)" >&2
        return 1
    fi

    if ! command -v openssl &>/dev/null; then
        error "inspect_ssl_cert: 'openssl' no está instalado (sudo apt install openssl)" >&2
        return 1
    fi

    local host port
    read -r host port <<< "$(_ssl_parse_host_port "$input")"

    info "Conectando a ${host}:${port}..."
    echo ""

    local subj_issuer
    subj_issuer="$(_ssl_fetch_subject "$host" "$port")"
    if [[ -z "$subj_issuer" ]]; then
        error "inspect_ssl_cert: no se pudo obtener el certificado. ¿El host está disponible?" >&2
        return 1
    fi

    local subject issuer
    subject="$(echo "$subj_issuer" | grep ^subject | cut -d= -f2- | xargs)"
    issuer="$(echo "$subj_issuer"  | grep ^issuer  | cut -d= -f2- | xargs)"

    local dates
    dates="$(_ssl_fetch_dates "$host" "$port")"
    local not_before not_after
    not_before="$(echo "$dates" | grep notBefore | cut -d= -f2)"
    not_after="$(echo "$dates"  | grep notAfter  | cut -d= -f2)"

    local days_left
    days_left="$(_ssl_days_until_expiry "$not_after")"

    local sans
    sans="$(_ssl_fetch_san "$host" "$port")"

    echo -e "${PURPLE}════════ Certificado SSL/TLS ════════════════════${NC}"
    echo -e "  ${CYAN}Sujeto:${NC}        ${subject}"
    echo -e "  ${CYAN}Emisor:${NC}        ${issuer}"
    echo -e "  ${CYAN}Válido desde:${NC}  ${not_before}"
    echo -e "  ${CYAN}Válido hasta:${NC}  ${not_after}"
    echo ""

    if [[ $days_left -le 0 ]]; then
        echo -e "  ${RED}[x] CERTIFICADO EXPIRADO${NC}"
    elif [[ $days_left -le $_SSL_WARN_DAYS ]]; then
        echo -e "  ${YELLOW}[!] Expira en ${days_left} días -- renovar pronto${NC}"
    else
        echo -e "  ${GREEN}[ok] Válido -- expira en ${days_left} días${NC}"
    fi

    echo ""
    echo -e "  ${CYAN}SANs:${NC}"
    echo "$sans" | tr ' ' '\n' | grep -v '^$' | while IFS= read -r san; do
        echo -e "    ${GREEN}*${NC} ${san}"
    done

    echo ""
    echo -e "${PURPLE}════════ Cadena de confianza ════════════════════${NC}"
    _ssl_fetch_chain "$host" "$port"

    echo ""
    echo -e "${PURPLE}════════ Versiones TLS aceptadas ════════════════${NC}"
    _ssl_check_tls_version "$host" "$port" "-tls1"   "TLS 1.0"
    _ssl_check_tls_version "$host" "$port" "-tls1_1" "TLS 1.1"
    echo -e "  ${GREEN}[ok]${NC} TLS 1.2 / 1.3 (estándar)"
    echo -e "${PURPLE}═════════════════════════════════════════════════${NC}"
}

# Ejecutar si se llama directamente
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
    inspect_ssl_cert "$@"
fi
