#!/bin/bash
# Minecraft Sessions & Coordinates Tracker
# Simple linear logic: processes events chronologically and maintains user sessions
# Creates individual JSON files for each session
# Designed to run every 5 minutes via cron or Django

# Configuration
BASE_DIR="/home/minecraft-reader/MCLOG_BACKAND"
EVENTS_LOG="$BASE_DIR/OUTPUT/minecraft_events.log"
SESSIONS_DIR="$BASE_DIR/OUTPUT/sessions"
INDEX_FILE="$BASE_DIR/OUTPUT/sessions_index.json"
LAST_TIMESTAMP_FILE="$BASE_DIR/LAST_READ_POS/sessions_last_timestamp.txt"
LAST_LINE_FILE="$BASE_DIR/LAST_READ_POS/sessions_last_line.txt"
AMP_COMMAND_SCRIPT="$BASE_DIR/SCRIPTS/amp_command.sh"

# Manual
show_help() {
   cat << HELP
Minecraft Sessions & Coordinates Tracker - Manual

DESCRIPTION:
 Simple linear processing of minecraft_events.log to create session files.
 Logic: Spawn -> Create session, Teleport -> Add coordinate, Disconnect -> Close session
 Creates one JSON file per session in the sessions/ directory.
 Maintains a sessions_index.json with overview of completed sessions.

USAGE:
 $0                          # Process new events (incremental)
 $0 --help                   # Show this manual
 $0 --rebuild                # Rebuild all sessions from scratch

LINEAR LOGIC:
 1. Process events chronologically (spawn -> teleport -> disconnect)
 2. Maintain map: username -> current_session_id
 3. Spawn: Create new session file, update map
 4. Teleport: Add coordinate to current session for that user
 5. Disconnect: Finalize session, remove from map
 6. Execute TP commands for users with active sessions

SESSION FILE FORMAT:
 {
   "session_id": "Lese88_19-06-2025_17-16-44_981",
   "username": "Lese88",
   "xuid": "2533274931917301",
   "start_time": "19-06-2025 17:16:44:981 CEST",
   "end_time": null,
   "duration_minutes": 0,
   "status": "active",
   "coordinates": [
     {"timestamp": "19-06-2025 17:18:09:726 CEST", "x": 4692.82, "y": -56.00, "z": -4699.03}
   ]
 }

SETUP CRON:
 */5 * * * * $0              # Every 5 minutes

FILES:
 Input:  $EVENTS_LOG
 Output: $SESSIONS_DIR/*.json + $INDEX_FILE
 State:  $LAST_TIMESTAMP_FILE + $LAST_LINE_FILE
HELP
}

# Create directories
mkdir -p "$BASE_DIR/OUTPUT"
mkdir -p "$SESSIONS_DIR"
mkdir -p "$BASE_DIR/LAST_READ_POS"

# Extract timestamp from event line
extract_timestamp() {
    local line="$1"
    # Match format: [DD-MM-YYYY HH:MM:SS:mmm CEST]
    if [[ $line =~ ^\[([0-9]{2}-[0-9]{2}-[0-9]{4}\ [0-9]{2}:[0-9]{2}:[0-9]{2}:[0-9]{3}\ CEST)\] ]]; then
        echo "${BASH_REMATCH[1]}"
    else
        echo ""
    fi
}

# Convert timestamp to comparable format (Unix timestamp + milliseconds)
timestamp_to_comparable() {
    local timestamp="$1"  # "19-06-2025 17:16:44:981 CEST"

    if [[ $timestamp =~ ^([0-9]{2})-([0-9]{2})-([0-9]{4})\ ([0-9]{2}):([0-9]{2}):([0-9]{2}):([0-9]{3})\ CEST$ ]]; then
        local day="${BASH_REMATCH[1]}"
        local month="${BASH_REMATCH[2]}"
        local year="${BASH_REMATCH[3]}"
        local hour="${BASH_REMATCH[4]}"
        local minute="${BASH_REMATCH[5]}"
        local second="${BASH_REMATCH[6]}"
        local ms="${BASH_REMATCH[7]}"

        local date_str="${year}-${month}-${day} ${hour}:${minute}:${second}"
        local unix_timestamp=$(date -d "$date_str" +%s 2>/dev/null)

        if [[ -n "$unix_timestamp" ]]; then
            printf "%s%03d" "$unix_timestamp" "$ms"
        else
            echo "0"
        fi
    else
        echo "0"
    fi
}

# Get last processed line number
get_last_line() {
    if [[ -f "$LAST_LINE_FILE" ]]; then
        cat "$LAST_LINE_FILE" 2>/dev/null || echo "0"
    else
        echo "0"
    fi
}

# Save last processed line number
save_last_line() {
    local line_number="$1"
    echo "$line_number" > "$LAST_LINE_FILE"
    echo "$(date): Updated last processed line to: $line_number"
}

# Get last processed timestamp
get_last_timestamp() {
    if [[ -f "$LAST_TIMESTAMP_FILE" ]]; then
        cat "$LAST_TIMESTAMP_FILE" 2>/dev/null || echo ""
    else
        echo ""
    fi
}

# Save last processed timestamp
save_last_timestamp() {
    local timestamp="$1"
    echo "$timestamp" > "$LAST_TIMESTAMP_FILE"
    echo "$(date): Updated last processed timestamp to: $timestamp"
}

# Load existing active sessions to restore user mapping
load_active_sessions() {
    declare -gA current_session_for_user

    echo "$(date): Loading active sessions..."
    local active_count=0

    for session_file in "$SESSIONS_DIR"/*.json; do
        if [[ -f "$session_file" ]] && grep -q '"status": "active"' "$session_file"; then
            local session_id=$(basename "$session_file" .json)
            local username=$(grep '"username"' "$session_file" | sed 's/.*"username": "\([^"]*\)".*/\1/')

            if [[ -n "$username" ]]; then
                current_session_for_user["$username"]="$session_id"
                ((active_count++))
                echo "$(date): Restored active session: $username -> $session_id"
            fi
        fi
    done

    echo "$(date): Loaded $active_count active sessions"
}

# Generate session ID
generate_session_id() {
    local username="$1"
    local timestamp="$2"  # Format: "19-06-2025 17:16:44:981 CEST"

    # Sanitize username: replace spaces with underscores for filename safety
    local safe_username=$(echo "$username" | tr ' ' '_')

    # Extract date and time components
    if [[ $timestamp =~ ^([0-9]{2}-[0-9]{2}-[0-9]{4})\ ([0-9]{2}:[0-9]{2}:[0-9]{2}):([0-9]{3})\ CEST$ ]]; then
        local date="${BASH_REMATCH[1]}"
        local time="${BASH_REMATCH[2]}"
        local ms="${BASH_REMATCH[3]}"

        # Format: Username_DD-MM-YYYY_HH-MM-SS_MS
        local time_formatted=$(echo "$time" | sed 's/:/-/g')
        echo "${safe_username}_${date}_${time_formatted}_${ms}"
    else
        # Fallback
        local fallback_ts=$(TZ='Europe/Rome' date '+%d-%m-%Y_%H-%M-%S')
        echo "${safe_username}_${fallback_ts}_fallback"
    fi
}

# Calculate duration in minutes
calculate_duration() {
    local start_time="$1"  # "19-06-2025 17:16:44:981 CEST"
    local end_time="$2"    # "19-06-2025 17:20:30:123 CEST"

    local start_clean=$(echo "$start_time" | sed 's/:\([0-9]\{3\}\) CEST$//' | sed 's/\([0-9]\{2\}\)-\([0-9]\{2\}\)-\([0-9]\{4\}\)/\3-\2-\1/')
    local end_clean=$(echo "$end_time" | sed 's/:\([0-9]\{3\}\) CEST$//' | sed 's/\([0-9]\{2\}\)-\([0-9]\{2\}\)-\([0-9]\{4\}\)/\3-\2-\1/')

    local start_unix=$(date -d "$start_clean" +%s 2>/dev/null)
    local end_unix=$(date -d "$end_clean" +%s 2>/dev/null)

    if [[ -n "$start_unix" && -n "$end_unix" ]]; then
        local duration_seconds=$((end_unix - start_unix))
        local duration_minutes=$((duration_seconds / 60))
        echo "$duration_minutes"
    else
        echo "0"
    fi
}

# Create new session file
create_session_file() {
    local session_id="$1"
    local username="$2"
    local xuid="$3"
    local start_time="$4"

    local session_file="$SESSIONS_DIR/${session_id}.json"

    cat > "$session_file" << EOF
{
  "session_id": "$session_id",
  "username": "$username",
  "xuid": "$xuid",
  "start_time": "$start_time",
  "end_time": null,
  "duration_minutes": 0,
  "status": "active",
  "coordinates": []
}
EOF

    echo "$(date): Created session $session_id for $username"
}

# Add coordinate to session file
add_coordinate_to_session() {
    local session_id="$1"
    local timestamp="$2"
    local x="$3"
    local y="$4"
    local z="$5"
    local dimension="${6:-0}"

    local session_file="$SESSIONS_DIR/${session_id}.json"

    if [[ ! -f "$session_file" ]]; then
        echo "$(date): Warning - session file not found: $session_file"
        return 1
    fi

    local coord_obj="{\"timestamp\":\"$timestamp\",\"x\":$x,\"y\":$y,\"z\":$z,\"dimension\":$dimension}"

    if grep -q '"coordinates": \[\]' "$session_file"; then
        sed -i 's/"coordinates": \[\]/"coordinates": ['"$coord_obj"']/' "$session_file"
    else
        sed -i 's/\(.*\)\]$/\1,'"$coord_obj"']/' "$session_file"
    fi

    echo "$(date): Added coordinate to $session_id: ($x, $y, $z) dim=$dimension"
}

# Finalize session (mark as completed)
finalize_session() {
    local session_id="$1"
    local end_time="$2"

    local session_file="$SESSIONS_DIR/${session_id}.json"

    if [[ ! -f "$session_file" ]]; then
        echo "$(date): Warning - cannot finalize session file: $session_file"
        return 1
    fi

    local start_time=$(grep '"start_time"' "$session_file" | sed 's/.*"start_time": "\([^"]*\)".*/\1/')
    local duration=$(calculate_duration "$start_time" "$end_time")

    sed -i 's/"end_time": null/"end_time": "'"$end_time"'"/' "$session_file"
    sed -i 's/"duration_minutes": 0/"duration_minutes": '"$duration"'/' "$session_file"
    sed -i 's/"status": "active"/"status": "completed"/' "$session_file"

    echo "$(date): Finalized session $session_id: $duration minutes"
}

# AMP API credentials (same as amp_command.sh)
AMP_URL="http://127.0.0.1:8083"
AMP_USER="admin"
AMP_PASS="Prosciuttomarmellata88?"
AMP_HEADERS=(-H "Content-Type: application/json" -H "Accept: application/json")

# Login to AMP and return session ID
amp_login() {
    local response
    response=$(curl -s -X POST "$AMP_URL/API/Core/Login" \
        "${AMP_HEADERS[@]}" \
        -d "{\"username\":\"$AMP_USER\",\"password\":\"$AMP_PASS\",\"token\":\"\",\"rememberMe\":false}")
    echo "$response" | grep -o '"sessionID":"[^"]*' | cut -d'"' -f4
}

# Query player position and dimension via querytarget + GetUpdates
query_player_position() {
    local username="$1"

    local session_id
    session_id=$(amp_login)
    if [[ -z "$session_id" ]]; then
        echo "$(date): Warning - AMP login failed for querytarget ($username)"
        return 1
    fi

    # Send querytarget
    curl -s -X POST "$AMP_URL/API/Core/SendConsoleMessage" \
        "${AMP_HEADERS[@]}" \
        -d "{\"message\":\"querytarget @a[name=\\\"$username\\\"]\",\"SESSIONID\":\"$session_id\"}" > /dev/null

    # Aspetta che Minecraft processi il comando
    sleep 2

    # Leggi l'output della console
    local updates
    updates=$(curl -s -X POST "$AMP_URL/API/Core/GetUpdates" \
        "${AMP_HEADERS[@]}" \
        -d "{\"SESSIONID\":\"$session_id\"}")

    # Cerca dimension direttamente nel raw JSON (funziona anche con escape)
    local x y z dimension
    x=$(echo "$updates"         | grep -o '"x"[[:space:]]*:[[:space:]]*-\?[0-9.]*' | tail -1 | grep -o '-\?[0-9.]*$')
    y=$(echo "$updates"         | grep -o '"y"[[:space:]]*:[[:space:]]*-\?[0-9.]*' | tail -1 | grep -o '-\?[0-9.]*$')
    z=$(echo "$updates"         | grep -o '"z"[[:space:]]*:[[:space:]]*-\?[0-9.]*' | tail -1 | grep -o '-\?[0-9.]*$')
    dimension=$(echo "$updates" | grep -o '"dimension"[[:space:]]*:[[:space:]]*[0-9]*' | tail -1 | grep -o '[0-9]*$')

    if [[ -z "$x" || -z "$y" || -z "$z" ]]; then
        echo "$(date): Warning - could not parse position for $username (offline o errore)"
        return 1
    fi

    dimension="${dimension:-0}"
    local dim_name
    case "$dimension" in
        0) dim_name="OVERWORLD" ;;
        1) dim_name="NETHER" ;;
        2) dim_name="END" ;;
        *) dim_name="UNKNOWN" ;;
    esac

    echo "$(date): $username @ ($x, $y, $z) [$dim_name]"

    local sess="${current_session_for_user[$username]}"
    if [[ -n "$sess" ]]; then
        local timestamp=$(TZ='Europe/Rome' date '+%d-%m-%Y %H:%M:%S CEST')
        add_coordinate_to_session "$sess" "$timestamp" "$x" "$y" "$z" "$dimension"
    else
        echo "$(date): Warning - no active session for $username"
    fi
}

# Update sessions index with completed sessions
update_sessions_index() {
    local completed_sessions=()
    local total_sessions=0

    for session_file in "$SESSIONS_DIR"/*.json; do
        if [[ -f "$session_file" ]] && grep -q '"status": "completed"' "$session_file"; then
            local session_id=$(basename "$session_file" .json)
            local username=$(grep '"username"' "$session_file" | sed 's/.*"username": "\([^"]*\)".*/\1/')
            local start_time=$(grep '"start_time"' "$session_file" | sed 's/.*"start_time": "\([^"]*\)".*/\1/')
            local end_time=$(grep '"end_time"' "$session_file" | sed 's/.*"end_time": "\([^"]*\)".*/\1/')
            local duration=$(grep '"duration_minutes"' "$session_file" | sed 's/.*"duration_minutes": \([^,]*\).*/\1/')
            local coords_count=$(grep -o '"timestamp"' "$session_file" | wc -l)

            local session_summary="{\"session_id\":\"$session_id\",\"username\":\"$username\",\"start_time\":\"$start_time\",\"end_time\":\"$end_time\",\"duration_minutes\":$duration,\"status\":\"completed\",\"coordinates_count\":$coords_count}"
            completed_sessions+=("$session_summary")
            ((total_sessions++))
        fi
    done

    local current_time=$(TZ='Europe/Rome' date '+%d-%m-%Y %H:%M:%S CEST')
    local temp_index="${INDEX_FILE}.tmp"

    cat > "$temp_index" << EOF
{
  "timestamp": "$current_time",
  "total_sessions": $total_sessions,
  "sessions": [
EOF

    if [[ $total_sessions -gt 0 ]]; then
        local first=true
        for session in "${completed_sessions[@]}"; do
            if [[ "$first" == true ]]; then
                echo "    $session" >> "$temp_index"
                first=false
            else
                echo "    ,$session" >> "$temp_index"
            fi
        done
    fi

    cat >> "$temp_index" << EOF
  ]
}
EOF

    mv "$temp_index" "$INDEX_FILE"
    echo "$(date): Updated index with $total_sessions completed sessions"
}

# Query positions for all active sessions
execute_coordinate_updates() {
    local active_users=()

    for session_file in "$SESSIONS_DIR"/*.json; do
        if [[ -f "$session_file" ]] && grep -q '"status": "active"' "$session_file"; then
            local username=$(grep '"username"' "$session_file" | sed 's/.*"username": "\([^"]*\)".*/\1/')
            active_users+=("$username")
        fi
    done

    if [[ ${#active_users[@]} -eq 0 ]]; then
        echo "$(date): No active sessions found"
        return 0
    fi

    echo "$(date): Found ${#active_users[@]} active sessions: ${active_users[*]}"

    for username in "${active_users[@]}"; do
        query_player_position "$username"
        sleep 1
    done

    echo "$(date): Coordinate update completed"
}

# Main processing function
process_events() {
    local rebuild_mode="$1"

    declare -gA current_session_for_user

    local start_line=1
    local last_line_processed=0
    local events_processed=0
    local last_event_timestamp=""

    if [[ "$rebuild_mode" == "rebuild" ]]; then
        echo "$(date): Rebuilding all sessions from scratch"
        rm -f "$SESSIONS_DIR"/*.json
        rm -f "$LAST_LINE_FILE"
        rm -f "$LAST_TIMESTAMP_FILE"
        start_line=1
    else
        load_active_sessions
        last_line_processed=$(get_last_line)
        start_line=$((last_line_processed + 1))
        echo "$(date): Starting incremental processing from line $start_line"
    fi

    if [[ ! -f "$EVENTS_LOG" ]]; then
        echo "$(date): Events log not found: $EVENTS_LOG" >&2
        return 1
    fi

    local total_lines=$(wc -l < "$EVENTS_LOG")
    echo "$(date): Processing lines $start_line to $total_lines (total: $((total_lines - start_line + 1)) new lines)"

    local current_line=0
    while IFS= read -r line; do
        ((current_line++))

        if [[ $current_line -lt $start_line ]]; then
            continue
        fi

        if [[ -z "$line" ]]; then continue; fi

        local current_timestamp=$(extract_timestamp "$line")
        if [[ -z "$current_timestamp" ]]; then
            echo "$(date): Warning - no timestamp found in line $current_line: $line"
            continue
        fi

        last_event_timestamp="$current_timestamp"
        ((events_processed++))

        echo "$(date): Processing line $current_line: [${current_timestamp}]"

        # Parse Player Spawned events
        # Use parameter expansion to extract username - avoids bash regex backtracking issues
        if [[ "$line" =~ ^\[([^\]]+)\]\ Player\ Spawned: ]]; then
            local timestamp="${BASH_REMATCH[1]}"
            local after_spawned="${line#*Player Spawned: }"
            local username="${after_spawned% xuid:*}"
            local xuid_part="${after_spawned#* xuid: }"
            local xuid="${xuid_part%%[^0-9]*}"

            if [[ -z "$username" || -z "$xuid" ]]; then
                echo "$(date): Warning - could not parse Spawned fields in line $current_line"
                continue
            fi

            local existing_session="${current_session_for_user[$username]}"
            if [[ -n "$existing_session" ]]; then
                echo "$(date): Warning - User $username spawned while having active session $existing_session, closing it"
                finalize_session "$existing_session" "$timestamp"
            fi

            local session_id=$(generate_session_id "$username" "$timestamp")
            create_session_file "$session_id" "$username" "$xuid" "$timestamp"

            current_session_for_user["$username"]="$session_id"

            echo "$(date): Started session for $username: $session_id"

        # Parse Teleport events
        # Use parameter expansion to extract username - avoids bash regex backtracking issues
        elif [[ "$line" =~ ^\[([^\]]+)\]\ Teleport: ]]; then
            local timestamp="${BASH_REMATCH[1]}"
            local after_teleport="${line#*Teleport: }"
            local username="${after_teleport% X=*}"
            local coords_part="${after_teleport#* X=}"
            local x="${coords_part%% *}"
            coords_part="${coords_part#* Y=}"
            local y="${coords_part%% *}"
            coords_part="${coords_part#* Z=}"
            local z="${coords_part%% *}"

            if [[ -z "$username" || -z "$x" || -z "$y" || -z "$z" ]]; then
                echo "$(date): Warning - could not parse Teleport fields in line $current_line"
                continue
            fi

            local session_id="${current_session_for_user[$username]}"
            if [[ -n "$session_id" ]]; then
                add_coordinate_to_session "$session_id" "$timestamp" "$x" "$y" "$z"
            else
                echo "$(date): Warning - teleport for $username without active session"
            fi

        # Parse Player disconnected events - captures up to "," so already handles spaces correctly
        elif [[ "$line" =~ ^\[([^\]]+)\]\ Player\ disconnected:\ ([^,]+),\ xuid:\ ([0-9]+) ]]; then
            local timestamp="${BASH_REMATCH[1]}"
            local username="${BASH_REMATCH[2]}"
            local xuid="${BASH_REMATCH[3]}"

            echo "$(date): Found disconnect event for: '$username' at '$timestamp'"

            local session_id="${current_session_for_user[$username]}"
            if [[ -n "$session_id" ]]; then
                echo "$(date): Finalizing session: '$session_id'"
                finalize_session "$session_id" "$timestamp"
                unset current_session_for_user["$username"]
                echo "$(date): Ended session for $username: $session_id"
            else
                echo "$(date): Warning - disconnect for $username without active session"
            fi
        fi

        # Save progress every 100 lines
        if [[ $((events_processed % 100)) -eq 0 ]]; then
            save_last_line "$current_line"
            save_last_timestamp "$last_event_timestamp"
        fi

    done < "$EVENTS_LOG"

    echo "$(date): Processed $events_processed new events"

    update_sessions_index

    execute_coordinate_updates

    if [[ $events_processed -gt 0 ]]; then
        save_last_line "$current_line"
        save_last_timestamp "$last_event_timestamp"
    fi

    echo "$(date): Processing completed. Active sessions remaining:"
    for user in "${!current_session_for_user[@]}"; do
        echo "$(date):   $user -> ${current_session_for_user[$user]}"
    done
}

# Main function
main() {
    case "$1" in
        --help|-h)
            show_help
            exit 0
            ;;
        --rebuild)
            if [[ ! -f "$EVENTS_LOG" ]]; then
                echo "$(date): Events log not found: $EVENTS_LOG" >&2
                exit 1
            fi
            process_events "rebuild"
            ;;
        *)
            if [[ ! -f "$EVENTS_LOG" ]]; then
                echo "$(date): Events log not found: $EVENTS_LOG" >&2
                exit 1
            fi
            process_events "incremental"
            ;;
    esac
}

main "$@"
