#!/bin/bash

# ============================================================
# PERC Disk Health Check
# RAID + JBOD
# StorCLI + smartctl
# READ ONLY / production safe
# ============================================================

STORCLI=$(command -v storcli64 || command -v storcli)

if [[ -z "$STORCLI" ]]; then
    echo "ERROR: storcli/storcli64 tidak ditemukan"
    exit 1
fi

if ! command -v smartctl >/dev/null 2>&1; then
    echo "ERROR: smartctl tidak ditemukan"
    exit 1
fi

CTRL=0

# ------------------------------------------------------------
# StorCLI summary
# ------------------------------------------------------------

STOR_SUM=$($STORCLI /c${CTRL}/eall/sall show 2>/dev/null)

# ------------------------------------------------------------
# Linux physical disk mapping
# ------------------------------------------------------------

declare -A DEV_BY_SERIAL

while read -r name type; do
    [[ "$type" == "disk" ]] || continue

    # Skip ZFS zvol
    [[ "$name" =~ ^zd ]] && continue

    dev="/dev/$name"
    [[ -b "$dev" ]] || continue

    model=$(lsblk -dn -o MODEL "$dev" 2>/dev/null | xargs)

    # Skip PERC virtual/controller disks
    if echo "$model" | grep -qiE 'PERC|MegaRAID|Virtual Disk'; then
        continue
    fi

    serial=$(lsblk -dn -o SERIAL "$dev" 2>/dev/null | xargs)

    [[ -n "$serial" ]] && DEV_BY_SERIAL["$serial"]="$dev"

done < <(lsblk -dn -o NAME,TYPE)

# ------------------------------------------------------------
# Header
# ------------------------------------------------------------

printf "%-7s %-6s %-10s %-18s %-8s %-10s %-18s %-18s %-6s %-6s %-6s %-6s %-7s %-7s %-7s %-6s %-7s\n" \
"SLOT" "STATE" "DEVICE" "MODEL" "BRAND" "SIZE" "SERIAL" "WWN" \
"MEDIA" "OTHER" "PRED" "SMART" "GROWN" "READ" "VERIFY" "TEMP" "STATUS"

printf '%*s\n' 169 '' | tr ' ' '-'

# ------------------------------------------------------------
# Get unique physical drive paths
# Important: sort -u prevents duplicate slot output
# ------------------------------------------------------------

mapfile -t DRIVE_PATHS < <(
    $STORCLI /c${CTRL}/eall/sall show all 2>/dev/null |
    sed -n 's/^Drive \(\/c[0-9]*\/e[0-9]*\/s[0-9]*\).*/\1/p' |
    sort -u -V
)

# ------------------------------------------------------------
# Process physical disks
# ------------------------------------------------------------

for path in "${DRIVE_PATHS[@]}"; do

    EID=$(echo "$path" | sed -n 's#.*\/e\([0-9]*\)\/s.*#\1#p')
    SLOTNO=$(echo "$path" | sed -n 's#.*\/s\([0-9]*\).*#\1#p')

    SLOT="${EID}:${SLOTNO}"

    # IMPORTANT:
    # Query individual physical disk instead of parsing giant show-all output.
    PD=$($STORCLI "$path" show all 2>/dev/null)

    # --------------------------------------------------------
    # State
    # --------------------------------------------------------

    STATE=$(echo "$STOR_SUM" | awk -v slot="$SLOT" '
        $1 == slot {
            print $3
            exit
        }
    ')

    [[ -z "$STATE" ]] && STATE="-"

    # --------------------------------------------------------
    # Serial
    # --------------------------------------------------------

    SERIAL=$(echo "$PD" | awk -F' = ' '
        /^[[:space:]]*SN =/ {
            print $2
            exit
        }
    ' | xargs)

    # --------------------------------------------------------
    # WWN
    # --------------------------------------------------------

    WWN=$(echo "$PD" | awk -F' = ' '
        /^[[:space:]]*WWN =/ {
            print $2
            exit
        }
    ' | xargs)

    # Some StorCLI versions use SAS Address
    if [[ -z "$WWN" ]]; then
        WWN=$(echo "$PD" | awk -F' = ' '
            /^[[:space:]]*SAS Address =/ {
                print $2
                exit
            }
        ' | xargs)
    fi

    [[ -z "$WWN" ]] && WWN="-"

    # --------------------------------------------------------
    # Model
    # --------------------------------------------------------

    MODEL=$(echo "$PD" | awk -F' = ' '
        /^[[:space:]]*Model Number =/ {
            print $2
            exit
        }
    ' | xargs)

    # Fallback from StorCLI summary table
    if [[ -z "$MODEL" ]]; then
        MODEL=$(echo "$STOR_SUM" | awk -v slot="$SLOT" '
            $1 == slot {
                for (i=1; i<=NF; i++) {
                    if ($i ~ /^(ST|HUS|HUH|MZ|SAMSUNG|INTEL|WDC|TOSHIBA|HGST)/) {
                        print $i
                        exit
                    }
                }
            }
        ')
    fi

    [[ -z "$MODEL" ]] && MODEL="-"

    # --------------------------------------------------------
    # Brand from model prefix
    # --------------------------------------------------------

    case "$MODEL" in
        ST*|SEAGATE*)        BRAND="Seagate" ;;
        HUS*|HUH*|HGST*)     BRAND="HGST" ;;
        MZ*|SAMSUNG*)        BRAND="Samsung" ;;
        INTEL*)              BRAND="Intel" ;;
        WDC|WD*)             BRAND="WDC" ;;
        TOSHIBA*|MK*)        BRAND="Toshiba" ;;
        KIOXIA*|KX*)         BRAND="Kioxia" ;;
        MTFD*)               BRAND="Micron" ;;
        SDL*)                BRAND="Dell" ;;
        *)                   BRAND="-" ;;
    esac

    # --------------------------------------------------------
    # Size (raw, fallback coerced)
    # --------------------------------------------------------

    SIZE=$(echo "$PD" | awk -F' = ' '
        /^[[:space:]]*Raw size =/ {
            print $2
            exit
        }
    ' | awk '{print $1, $2}')

    [[ -z "$SIZE" ]] && \
    SIZE=$(echo "$PD" | awk -F' = ' '
        /^[[:space:]]*Coerced size =/ {
            print $2
            exit
        }
    ' | awk '{print $1, $2}')

    [[ -z "$SIZE" ]] && SIZE="-"

    # --------------------------------------------------------
    # StorCLI error counters
    # --------------------------------------------------------

    MEDIA=$(echo "$PD" | awk -F' = ' '
        /Media Error Count/ {
            print $2
            exit
        }
    ' | xargs)

    OTHER=$(echo "$PD" | awk -F' = ' '
        /Other Error Count/ {
            print $2
            exit
        }
    ' | xargs)

    PRED=$(echo "$PD" | awk -F' = ' '
        /Predictive Failure Count/ {
            print $2
            exit
        }
    ' | xargs)

    SMART_ALERT=$(echo "$PD" | awk -F' = ' '
        /S.M.A.R.T alert flagged by drive/ {
            print $2
            exit
        }
    ' | xargs)

    TEMP_STOR=$(echo "$PD" | awk -F' = ' '
        /Drive Temperature/ {
            print $2
            exit
        }
    ' | awk '{print $1}')

    [[ -z "$TEMP_STOR" ]] && \
    TEMP_STOR=$(echo "$PD" | awk -F' = ' '
        /Temperature/ {
            print $2
            exit
        }
    ' | awk '{print $1}')

    [[ -n "$TEMP_STOR" ]] && TEMP_STOR="${TEMP_STOR%C}"

    [[ -z "$MEDIA" ]] && MEDIA="-"
    [[ -z "$OTHER" ]] && OTHER="-"
    [[ -z "$PRED" ]] && PRED="-"
    [[ -z "$SMART_ALERT" ]] && SMART_ALERT="-"

    # --------------------------------------------------------
    # Map to Linux /dev/sdX when exposed
    # --------------------------------------------------------

    DEVICE="-"

    if [[ -n "$SERIAL" && -n "${DEV_BY_SERIAL[$SERIAL]}" ]]; then
        DEVICE="${DEV_BY_SERIAL[$SERIAL]}"
    fi

    # --------------------------------------------------------
    # SMART enrichment for JBOD/direct exposed disks
    # --------------------------------------------------------

    GROWN="-"
    READ_ERR="-"
    VERIFY_ERR="-"
    if [[ -n "$TEMP_STOR" ]]; then
        TEMP="${TEMP_STOR}C"
    else
        TEMP="-"
    fi
    SMART_HEALTH=""

    if [[ "$DEVICE" != "-" ]]; then

        SMART_DATA=$(smartctl -a "$DEVICE" 2>/dev/null)

        SMART_HEALTH=$(echo "$SMART_DATA" | awk -F: '
            /SMART Health Status:/ {
                sub(/^[ \t]+/, "", $2)
                print $2
                exit
            }
        ')

        GROWN=$(echo "$SMART_DATA" | awk -F: '
            /Elements in grown defect list:/ {
                gsub(/ /, "", $2)
                print $2
                exit
            }
        ')

        READ_ERR=$(echo "$SMART_DATA" | awk '
            $1 == "read:" {
                print $NF
                exit
            }
        ')


        VERIFY_ERR=$(echo "$SMART_DATA" | awk '
            $1 == "verify:" {
                print $NF
                exit
            }
        ')

        SMART_TEMP=$(echo "$SMART_DATA" | awk '
            /Current Drive Temperature:/ {
                print $4
                exit
            }
        ')

        if [[ -n "$SMART_TEMP" ]]; then
            TEMP="${SMART_TEMP}C"
        fi

        [[ -z "$GROWN" ]] && GROWN="-"
        [[ -z "$READ_ERR" ]] && READ_ERR="-"
        [[ -z "$VERIFY_ERR" ]] && VERIFY_ERR="-"

        # WWN fallback from Linux
        if [[ "$WWN" == "-" ]]; then
            WWN=$(lsblk -dn -o WWN "$DEVICE" 2>/dev/null | xargs)

            if [[ -z "$WWN" ]]; then
                WWN=$(echo "$SMART_DATA" | awk -F: '
                    /Logical Unit id:/ {
                        sub(/^[ \t]+/, "", $2)
                        print $2
                        exit
                    }
                ')
            fi

            [[ -z "$WWN" ]] && WWN="-"
        fi
    fi

    # --------------------------------------------------------
    # Final health status
    # --------------------------------------------------------

STATUS="OK"

if \
    echo "$SMART_ALERT" | grep -qi '^yes$' || \
    { [[ "$PRED" =~ ^[0-9]+$ ]] && (( PRED > 0 )); } || \
    echo "$SMART_HEALTH" | grep -qiE 'fail|impending|bad'
then
    STATUS="FAIL"

elif \
    { [[ "$MEDIA" =~ ^[0-9]+$ ]] && (( MEDIA > 0 )); } || \
    { [[ "$GROWN" =~ ^[0-9]+$ ]] && (( GROWN > 0 )); } || \
    { [[ "$READ_ERR" =~ ^[0-9]+$ ]] && (( READ_ERR > 0 )); } || \
    { [[ "$VERIFY_ERR" =~ ^[0-9]+$ ]] && (( VERIFY_ERR > 0 )); }
then
    STATUS="WARN"

elif \
    [[ "$OTHER" =~ ^[0-9]+$ ]] && (( OTHER > 0 ))
then
    STATUS="INFO"
fi

    # --------------------------------------------------------
    # Print
    # --------------------------------------------------------

    printf "%-7s %-6s %-10s %-18s %-8s %-10s %-18s %-18s %-6s %-6s %-6s %-6s %-7s %-7s %-7s %-6s %-7s\n" \
    "$SLOT" \
    "$STATE" \
    "$DEVICE" \
    "${MODEL:0:18}" \
    "$BRAND" \
    "${SIZE:0:10}" \
    "${SERIAL:0:18}" \
    "${WWN:0:18}" \
    "$MEDIA" \
    "$OTHER" \
    "$PRED" \
    "$SMART_ALERT" \
    "$GROWN" \
    "$READ_ERR" \
    "$VERIFY_ERR" \
    "$TEMP" \
    "$STATUS"

done
