#!/bin/bash

#============================================================================
# Story Protocol - Advanced Validator Monitoring System
# Description: Comprehensive health checks for Story validators
#============================================================================

#============================================================================
# Terminal Colors and Formatting
#============================================================================
declare -r COLOR_ERROR='\033[0;31m'
declare -r COLOR_SUCCESS='\033[0;32m'
declare -r COLOR_WARNING='\033[1;33m'
declare -r COLOR_INFO='\033[0;34m'
declare -r COLOR_HEADER='\033[0;36m'
declare -r COLOR_RESET='\033[0m'

#============================================================================
# Global Configuration Variables
#============================================================================

# Network and RPC Configuration
declare -r CONFIG_FILE="$HOME/.story/story/config/config.toml"
RPC_PORT=$(grep -m 1 -oP '^laddr = "\K[^"]+' "$CONFIG_FILE" | cut -d ':' -f 3)
LOCAL_RPC_ENDPOINT="http://127.0.0.1:$RPC_PORT"
NETWORK_RPC_ENDPOINT="https://rpc.story.stakeup.tech"

# Monitoring Thresholds
THRESHOLD_BLOCK_DIFFERENCE=100
THRESHOLD_MINIMUM_PEERS=5
THRESHOLD_MISSED_BLOCKS=5
BLOCKS_TO_ANALYZE=100
THRESHOLD_VP_DECREASE=100000
THRESHOLD_VP_INCREASE=1000000

# Alert Configuration
ALERT_TELEGRAM_ENABLED=true
ALERT_ON_VP_CHANGES=true
TELEGRAM_API_TOKEN=""
TELEGRAM_CHAT_IDENTIFIER=""
NODE_IDENTIFIER="Story mainnet"

# File System Paths
LOG_DIR="/var/log/story_monitor"
VP_TRACKING_FILE="$LOG_DIR/voting_power_history.txt"
RANK_TRACKING_FILE="$LOG_DIR/rank_history.txt"

# Retry Configuration
API_MAX_ATTEMPTS=3
API_RETRY_WAIT=5

#============================================================================
# System Initialization
#============================================================================
initialize_monitoring_system() {
    # Ensure log directory exists
    if [[ ! -d "$LOG_DIR" ]]; then
        sudo mkdir -p "$LOG_DIR"
        sudo chown $USER:$USER "$LOG_DIR"
    fi

    # Set error handling
    set -euo pipefail
}

#============================================================================
# Output Functions
#============================================================================
log_message() {
    local severity="$1"
    shift
    local message="$*"
    local timestamp=$(date '+%Y-%m-%d %H:%M:%S')

    echo "[$timestamp] [$severity] $message"
}

display_message() {
    local color="$1"
    local message="$2"
    echo -e "${color}${message}${COLOR_RESET}"
}

print_section_header() {
    local title="$1"
    echo ""
    display_message "$COLOR_HEADER" "📋 $title"
    echo "----------------------------------------------------------------------------"
}

#============================================================================
# Telegram Integration
#============================================================================
dispatch_telegram_alert() {
    if [[ "$ALERT_TELEGRAM_ENABLED" != "true" ]]; then
        return 0
    fi

    local alert_content="$1"
    local silent_mode="${2:-false}"

    if [[ -z "$TELEGRAM_API_TOKEN" ]] || [[ -z "$TELEGRAM_CHAT_IDENTIFIER" ]]; then
        log_message "WARN" "Telegram configuration missing"
        return 1
    fi

    local notification_flag=""
    if [[ "$silent_mode" == "true" ]]; then
        notification_flag="&disable_notification=true"
    fi

    local api_response=$(curl -s -X POST \
        "https://api.telegram.org/bot$TELEGRAM_API_TOKEN/sendMessage" \
        -d "chat_id=$TELEGRAM_CHAT_IDENTIFIER" \
        -d "text=$alert_content" \
        -d "parse_mode=HTML" \
        $notification_flag)

    if echo "$api_response" | jq -e '.ok' > /dev/null 2>&1; then
        log_message "INFO" "Alert dispatched via Telegram"
        return 0
    else
        log_message "ERROR" "Telegram dispatch failed: $api_response"
        return 1
    fi
}

#============================================================================
# Network Communication Functions
#============================================================================
execute_api_request() {
    local endpoint="$1"
    local operation_name="$2"
    local attempt=0
    local result=""

    while [[ $attempt -lt $API_MAX_ATTEMPTS ]]; do
        result=$(curl -s --max-time 10 "$endpoint" 2>/dev/null || echo "")

        if [[ -n "$result" ]] && echo "$result" | jq empty 2>/dev/null; then
            echo "$result"
            return 0
        fi

        attempt=$((attempt + 1))
        if [[ $attempt -lt $API_MAX_ATTEMPTS ]]; then
            log_message "WARN" "$operation_name attempt $attempt/$API_MAX_ATTEMPTS failed, retrying..."
            sleep $API_RETRY_WAIT
        fi
    done

    log_message "ERROR" "$operation_name failed after $API_MAX_ATTEMPTS attempts"
    return 1
}

verify_network_rpc_status() {
    if curl --output /dev/null --silent --head --fail --max-time 5 \
        "$NETWORK_RPC_ENDPOINT/status" 2>/dev/null; then
        return 0
    else
        return 1
    fi
}

#============================================================================
# Core Node Health Checks
#============================================================================
validate_node_responsiveness() {
    local api_response=$(execute_api_request "${LOCAL_RPC_ENDPOINT}/status" "Node status query")

    if [[ -z "$api_response" ]]; then
        display_message "$COLOR_ERROR" "❌ Node connectivity failure detected"
        dispatch_telegram_alert "🚨 <b>STORY $NODE_IDENTIFIER</b>
Node is unresponsive. Immediate investigation required!"
        return 1
    fi

    # Extract critical metrics
    CURRENT_NODE_HEIGHT=$(echo "$api_response" | jq -r '.result.sync_info.latest_block_height')
    IS_NODE_SYNCING=$(echo "$api_response" | jq -r '.result.sync_info.catching_up')
    VALIDATOR_HEX_ADDRESS=$(echo "$api_response" | jq -r '.result.validator_info.address')
    CURRENT_VOTING_POWER=$(echo "$api_response" | jq -r '.result.validator_info.voting_power')

    display_message "$COLOR_RESET" "✓ Node responsive: height=$CURRENT_NODE_HEIGHT, syncing=$IS_NODE_SYNCING"

    return 0
}

analyze_synchronization_status() {
    if ! verify_network_rpc_status; then
        display_message "$COLOR_WARNING" "⚠ Network RPC unavailable, sync check skipped"
        return 0
    fi

    local network_data=$(execute_api_request "$NETWORK_RPC_ENDPOINT/status" "Network height query")
    if [[ -z "$network_data" ]]; then
        display_message "$COLOR_WARNING" "⚠ Unable to retrieve network height"
        return 0
    fi

    local network_chain_height=$(echo "$network_data" | jq -r '.result.sync_info.latest_block_height')
    local synchronization_gap=$((network_chain_height - CURRENT_NODE_HEIGHT))

    if [[ $synchronization_gap -ge $THRESHOLD_BLOCK_DIFFERENCE ]]; then
        display_message "$COLOR_ERROR" "❌ Synchronization lag: $synchronization_gap blocks (node: $CURRENT_NODE_HEIGHT, network: $network_chain_height)"
        dispatch_telegram_alert "🚨 <b>STORY $NODE_IDENTIFIER - Sync Alert</b>

📊 <b>Height Comparison:</b>
  • Local Node: $CURRENT_NODE_HEIGHT
  • Network: $network_chain_height
  • Lag: <b>$synchronization_gap blocks</b>

⚠️ Significant synchronization delay detected!
Action required immediately."
        return 1
    elif [[ $synchronization_gap -ge $((THRESHOLD_BLOCK_DIFFERENCE / 2)) ]]; then
        display_message "$COLOR_WARNING" "⚠ Synchronization warning: $synchronization_gap blocks lag"
    else
        echo "✓ Synchronization healthy (gap: $synchronization_gap blocks)"
    fi

    return 0
}

evaluate_peer_connectivity() {
    local network_data=$(execute_api_request "${LOCAL_RPC_ENDPOINT}/net_info" "Peer information query")

    if [[ -z "$network_data" ]]; then
        display_message "$COLOR_WARNING" "⚠ Peer data unavailable"
        return 0
    fi

    local active_peer_count=$(echo "$network_data" | jq -r '.result.n_peers // 0')

    if [[ "$active_peer_count" -lt $THRESHOLD_MINIMUM_PEERS ]]; then
        display_message "$COLOR_ERROR" "❌ Insufficient peers: $active_peer_count (minimum: $THRESHOLD_MINIMUM_PEERS)"
        dispatch_telegram_alert "⚠️ <b>STORY $NODE_IDENTIFIER - Connectivity Alert</b>

📡 <b>Peer Status:</b>
  • Connected: $active_peer_count
  • Required: $THRESHOLD_MINIMUM_PEERS

Low peer count may impact network synchronization."
        return 1
    else
        echo "✓ Peer connectivity healthy: $active_peer_count peers"
    fi

    return 0
}

#============================================================================
# Validator Performance Monitoring
#============================================================================
track_voting_power_changes() {
    if [[ "$ALERT_ON_VP_CHANGES" != "true" ]]; then
        return 0
    fi

    if [[ -z "$CURRENT_VOTING_POWER" ]] || [[ "$CURRENT_VOTING_POWER" == "0" ]]; then
        display_message "$COLOR_WARNING" "⚠ No voting power (validator not bonded)"
        return 0
    fi

    local historical_vp=""
    if [[ -f "$VP_TRACKING_FILE" ]]; then
        historical_vp=$(tail -1 "$VP_TRACKING_FILE" | awk '{print $3}')
    fi

    # Record current voting power
    echo "$(date '+%Y-%m-%d %H:%M:%S') $CURRENT_VOTING_POWER" >> "$VP_TRACKING_FILE"

    if [[ -z "$historical_vp" ]]; then
        echo "VP: $CURRENT_VOTING_POWER (initial measurement)"
        return 0
    fi

    local vp_delta=$((CURRENT_VOTING_POWER - historical_vp))

    # Alert on significant decrease
    if [[ $vp_delta -lt -$THRESHOLD_VP_DECREASE ]]; then
        local decrease_amount=$((vp_delta * -1))
        display_message "$COLOR_ERROR" "❌ VP: $CURRENT_VOTING_POWER (decreased by $decrease_amount)"
        dispatch_telegram_alert "⚠️ <b>STORY $NODE_IDENTIFIER - Voting Power Reduction</b>

📉 <b>Power Change Detected:</b>
  • Previous: $historical_vp
  • Current: $CURRENT_VOTING_POWER
  • Reduction: <b>-$decrease_amount</b>

Possible causes:
  • Slashing incident
  • Delegation withdrawal
  • Network rebalancing"
    elif [[ $vp_delta -gt $THRESHOLD_VP_INCREASE ]]; then
        echo "✓ VP: $CURRENT_VOTING_POWER (increased by $vp_delta)"
        dispatch_telegram_alert "📈 <b>STORY $NODE_IDENTIFIER - Voting Power Increase</b>

✅ <b>Power Change Detected:</b>
  • Previous: $historical_vp
  • Current: $CURRENT_VOTING_POWER
  • Increase: <b>+$vp_delta</b>

Significant new delegations received!"
    elif [[ $vp_delta -ne 0 ]]; then
        echo "VP: $CURRENT_VOTING_POWER (changed by $vp_delta)"
    else
        echo "✓ VP: $CURRENT_VOTING_POWER (stable)"
    fi

    # Maintain history file size
    if [[ -f "$VP_TRACKING_FILE" ]]; then
        tail -1000 "$VP_TRACKING_FILE" > "${VP_TRACKING_FILE}.tmp"
        mv "${VP_TRACKING_FILE}.tmp" "$VP_TRACKING_FILE"
    fi

    return 0
}

determine_validator_position() {
    if [[ -z "$VALIDATOR_HEX_ADDRESS" ]] || [[ "$VALIDATOR_HEX_ADDRESS" == "null" ]]; then
        display_message "$COLOR_WARNING" "⚠ Not operating as validator, rank analysis skipped"
        return 0
    fi

    if [[ -z "$CURRENT_VOTING_POWER" ]] || [[ "$CURRENT_VOTING_POWER" == "0" ]]; then
        display_message "$COLOR_WARNING" "⚠ No voting power, rank analysis skipped"
        return 0
    fi

    # Select appropriate RPC endpoint
    local rpc_endpoint="$LOCAL_RPC_ENDPOINT"
    if verify_network_rpc_status; then
        rpc_endpoint="$NETWORK_RPC_ENDPOINT"
    fi

    # Retrieve validator set
    local validator_set=$(execute_api_request "${rpc_endpoint}/validators?per_page=300" "Validator set query")

    if [[ -z "$validator_set" ]]; then
        display_message "$COLOR_WARNING" "⚠ Validator set unavailable"
        return 0
    fi

    local total_validator_count=$(echo "$validator_set" | jq -r '.result.total // 0')

    # Calculate rank position
    local validator_rank=0
    local found_validator=false

    local sorted_list=$(echo "$validator_set" | \
        jq -r '.result.validators | sort_by(-(.voting_power | tonumber)) | to_entries | .[] | "\(.key + 1)|\(.value.address)|\(.value.voting_power)"')

    while IFS='|' read -r position addr vp; do
        if [[ "$addr" == "$VALIDATOR_HEX_ADDRESS" ]]; then
            validator_rank=$position
            found_validator=true
            break
        fi
    done <<< "$sorted_list"

    if [[ "$found_validator" == false ]]; then
        display_message "$COLOR_WARNING" "⚠ Validator not in active set"
        return 0
    fi

    # Determine rank tier
    local rank_tier=""
    if [[ $validator_rank -le 10 ]]; then
        rank_tier="Elite Top 10"
    elif [[ $validator_rank -le 40 ]]; then
        rank_tier="Top 40"
    elif [[ $validator_rank -le 80 ]]; then
        rank_tier="Top 80"
    else
        rank_tier="Active Set"
    fi

    echo "✓ Position: #$validator_rank/$total_validator_count ($rank_tier)"

    # Track rank changes
    local previous_rank=""
    if [[ -f "$RANK_TRACKING_FILE" ]]; then
        previous_rank=$(tail -1 "$RANK_TRACKING_FILE" | awk '{print $3}')
    fi

    # Store current rank
    echo "$(date '+%Y-%m-%d %H:%M:%S') $validator_rank" >> "$RANK_TRACKING_FILE"

    if [[ -n "$previous_rank" ]]; then
        local rank_movement=$((previous_rank - validator_rank))

        # Alert on significant rank changes
        if [[ $rank_movement -ge 10 ]]; then
            dispatch_telegram_alert "📈 <b>STORY $NODE_IDENTIFIER - Rank Improvement</b>

🎯 <b>Position Update:</b>
  • Previous: #$previous_rank
  • Current: #$validator_rank
  • Improvement: <b>$rank_movement positions</b>
  • Total validators: $total_validator_count

Validator ranking improved significantly!"
        elif [[ $rank_movement -le -10 ]]; then
            local absolute_drop=$((rank_movement * -1))
            dispatch_telegram_alert "📉 <b>STORY $NODE_IDENTIFIER - Rank Decline</b>

⚠️ <b>Position Update:</b>
  • Previous: #$previous_rank
  • Current: #$validator_rank
  • Decline: <b>$absolute_drop positions</b>
  • Total validators: $total_validator_count

Validator ranking decreased."
        fi
    fi

    # Maintain history file size
    if [[ -f "$RANK_TRACKING_FILE" ]]; then
        tail -1000 "$RANK_TRACKING_FILE" > "${RANK_TRACKING_FILE}.tmp"
        mv "${RANK_TRACKING_FILE}.tmp" "$RANK_TRACKING_FILE"
    fi

    return 0
}

analyze_block_signatures() {
    if [[ -z "$VALIDATOR_HEX_ADDRESS" ]] || [[ "$VALIDATOR_HEX_ADDRESS" == "null" ]]; then
        display_message "$COLOR_WARNING" "⚠ Not operating as validator, signature analysis skipped"
        return 0
    fi

    if [[ -z "$CURRENT_NODE_HEIGHT" ]] || [[ "$CURRENT_NODE_HEIGHT" -lt $BLOCKS_TO_ANALYZE ]]; then
        display_message "$COLOR_WARNING" "⚠ Insufficient block history for signature analysis"
        return 0
    fi

    local blocks_signed=0
    local blocks_missed=0
    local consecutive_misses=0
    local max_consecutive_misses=0
    local analysis_start_block=$((CURRENT_NODE_HEIGHT - BLOCKS_TO_ANALYZE + 1))

    # Always use local RPC for block checks (faster and more reliable)
    local rpc_endpoint="$LOCAL_RPC_ENDPOINT"

    # Analyze blocks
    for ((block_num = analysis_start_block; block_num <= CURRENT_NODE_HEIGHT; block_num++)); do
        local block_data=""
        local retry_count=0
        local max_retries=3

        # Retry logic for each block
        while [[ $retry_count -lt $max_retries ]]; do
            block_data=$(curl -s --max-time 10 "${rpc_endpoint}/block?height=${block_num}" 2>&1)

            # Check if response is valid
            if [[ -n "$block_data" ]] && echo "$block_data" | jq -e '.result.block.last_commit.signatures' >/dev/null 2>&1; then
                break
            fi

            ((retry_count++))
            if [[ $retry_count -lt $max_retries ]]; then
                sleep 1
            fi
        done

        # If still failed after retries, log warning but continue
        if [[ $retry_count -eq $max_retries ]]; then
            write_log_entry "WARN" "Failed to fetch block $block_num after $max_retries attempts"
            # Count as missed to be conservative
            ((blocks_missed++))
            ((consecutive_misses++))
            if [[ $consecutive_misses -gt $max_consecutive_misses ]]; then
                max_consecutive_misses=$consecutive_misses
            fi
            continue
        fi

        local block_signatures=$(echo "$block_data" | jq -r ".result.block.last_commit.signatures[].validator_address // empty" 2>/dev/null)

        if echo "$block_signatures" | grep -q "$VALIDATOR_HEX_ADDRESS"; then
            ((blocks_signed++))
            consecutive_misses=0
        else
            ((blocks_missed++))
            ((consecutive_misses++))
            if [[ $consecutive_misses -gt $max_consecutive_misses ]]; then
                max_consecutive_misses=$consecutive_misses
            fi
        fi

        # Small delay to avoid rate limiting
        sleep 0.05
    done

    local miss_percentage=$((blocks_missed * 100 / BLOCKS_TO_ANALYZE))

    if [[ $blocks_missed -ge $THRESHOLD_MISSED_BLOCKS ]]; then
        display_message "$COLOR_ERROR" "❌ Block signatures: $blocks_missed/$BLOCKS_TO_ANALYZE missed ($miss_percentage%) | Max consecutive: $max_consecutive_misses"
        dispatch_telegram_alert "🚨 <b>STORY $NODE_IDENTIFIER - Signature Alert</b>

⚠️ <b>Signature Analysis (last $BLOCKS_TO_ANALYZE blocks):</b>
  • Signed: $blocks_signed
  • Missed: <b>$blocks_missed</b>
  • Miss Rate: <b>$miss_percentage%</b>
  • Max Consecutive: $max_consecutive_misses

Critical: High miss rate detected. Slashing risk!"
        return 1
    elif [[ $blocks_missed -ge $((THRESHOLD_MISSED_BLOCKS / 2)) ]]; then
        display_message "$COLOR_WARNING" "⚠ Block signatures: $blocks_missed/$BLOCKS_TO_ANALYZE missed ($miss_percentage%) | Max consecutive: $max_consecutive_misses"
    else
        echo "✓ Signature performance healthy: $blocks_missed/$BLOCKS_TO_ANALYZE missed ($miss_percentage%)"
    fi

    return 0
}

#============================================================================
# Main Execution Flow
#============================================================================
execute_monitoring_checks() {
    echo "============================================================================"
    display_message "$COLOR_HEADER" "Story Protocol Validator Monitor - $(date '+%Y-%m-%d %H:%M:%S')"
    echo "============================================================================"

    local overall_status=0

    # Phase 1: Core System Health
    print_section_header "Core System Health"
    validate_node_responsiveness || overall_status=1
    analyze_synchronization_status || overall_status=1

    # Phase 2: Validator Performance
    print_section_header "Validator Performance Metrics"
    track_voting_power_changes
    determine_validator_position
    analyze_block_signatures || overall_status=1
    evaluate_peer_connectivity || overall_status=1

    # Final Summary
    echo ""
    echo "============================================================================"
    if [[ $overall_status -eq 0 ]]; then
        display_message "$COLOR_SUCCESS" "✅ All monitoring checks completed successfully"
    else
        display_message "$COLOR_ERROR" "⚠️  Some checks reported issues - review above"
    fi
    echo "============================================================================"

    return $overall_status
}

#============================================================================
# Script Entry Point
#============================================================================
initialize_monitoring_system
execute_monitoring_checks

exit $?
