/*
 * ==================================================================================
 * State Machine Implementation - Protocol state management for client and server
 *
 * ISA Project - ICMP Covert Channel File Transfer System
 * Author: Roman Necas (xnecasr00)
 * Date: 13. October 2025
 *
 * DESCRIPTION:
 * This module implements event-driven state machines for both client and server
 * sides of the IFTP protocol. State machines ensure reliable file transfer by
 * managing protocol state transitions, timeout handling, error recovery, and retry logic.
 *
 * KEY FUNCTIONS:
 * Client State Machine:
 * - client_sm_init() - Initialize client state machine
 * - client_sm_transition() - Transition to new state with validation
 * - client_sm_handle_event() - Process protocol events (timeout, ACK, error)
 * - client_sm_check_timeout() - Check if current state has timed out
 * - client_sm_can_recover() - Determine if recovery is possible
 * - client_sm_reset() - Reset to initial state
 *
 * Server State Machine:
 * - server_sm_init() - Initialize server state machine
 * - server_sm_transition() - Transition to new state
 * - server_sm_handle_event() - Process protocol events (packet received, timeout)
 * - server_sm_check_timeout() - Check for idle timeout
 * - server_sm_can_recover() - Determine if recovery is possible
 * - server_sm_reset() - Reset to listening state
 *
 * CLIENT STATE MACHINE:
 * States:
 * - CLIENT_IDLE: Initial state, ready to send START
 * - CLIENT_START_SENT: START packet sent, waiting for START-ACK
 * - CLIENT_DATA_TRANSFER: Reading file and sending DATA packets
 * - CLIENT_DATA_WAIT_ACK: DATA packet sent, waiting for DATA-ACK
 * - CLIENT_END_SENT: END packet sent, waiting for END-ACK
 * - CLIENT_COMPLETED: Transfer completed successfully
 * - CLIENT_ERROR: Unrecoverable error occurred
 * - CLIENT_ABORT: User aborted transfer
 *
 * State Flow:
 * IDLE → START_SENT → DATA_TRANSFER ⇄ DATA_WAIT_ACK → END_SENT → COMPLETED
 *                ↓           ↓               ↓             ↓
 *              ERROR       ERROR           ERROR        ERROR
 *
 * Events:
 * - EVENT_PACKET_SENT: Packet successfully sent
 * - EVENT_ACK_RECEIVED: ACK packet received
 * - EVENT_TIMEOUT: State timeout expired
 * - EVENT_FILE_EOF: End of file reached
 * - EVENT_ERROR: Error occurred
 * - EVENT_USER_ABORT: User requested abort
 *
 * Timeout Handling (Client):
 * - Initial timeout: 2 seconds (INITIAL_TIMEOUT)
 * - Exponential backoff: Doubles on each retry (up to MAX_TIMEOUT = 10s)
 * - Maximum retries: 5 attempts (MAX_RETRIES)
 * - After max retries: Transition to CLIENT_ERROR
 *
 * Example Client Flow:
 * 1. IDLE: Initialize, prepare to send
 * 2. START_SENT: Send START, start timeout timer
 * 3. Wait for ACK (2s timeout):
 *    - If ACK received → DATA_TRANSFER
 *    - If timeout → Retry (up to 5 times with backoff)
 *    - If max retries → ERROR
 * 4. DATA_TRANSFER: Read chunk, encrypt, send DATA
 * 5. DATA_WAIT_ACK: Wait for DATA-ACK (2s timeout)
 *    - If ACK received → DATA_TRANSFER (next chunk)
 *    - If timeout → Retry current chunk
 * 6. Repeat steps 4-5 until file complete
 * 7. END_SENT: Send END, wait for END-ACK
 * 8. COMPLETED: Transfer successful
 *
 * SERVER STATE MACHINE:
 * States:
 * - SERVER_LISTENING: Waiting for START packet
 * - SERVER_SESSION_INIT: START received, sending START-ACK
 * - SERVER_RECEIVING_DATA: Receiving DATA packets
 * - SERVER_DATA_ACK_SENT: DATA-ACK sent, expecting next DATA
 * - SERVER_SESSION_CLOSING: END received, sending END-ACK
 * - SERVER_COMPLETED: Transfer completed successfully
 * - SERVER_ERROR: Error occurred
 * - SERVER_TIMEOUT: Session idle timeout
 *
 * State Flow:
 * LISTENING → SESSION_INIT → RECEIVING_DATA ⇄ DATA_ACK_SENT → SESSION_CLOSING → COMPLETED → LISTENING
 *      ↑          ↓                ↓                ↓                ↓                ↓
 *      └─────── ERROR/TIMEOUT ─────┴────────────────┴────────────────┴────────────────┘
 *
 * Events:
 * - EVENT_PACKET_RECEIVED: IFTP packet received
 * - EVENT_PACKET_SENT: ACK packet sent
 * - EVENT_TIMEOUT: Session idle timeout
 * - EVENT_ERROR: Error occurred
 *
 * Timeout Handling (Server):
 * - Idle timeout: 60 seconds (no packet received)
 * - Prevents resource leaks from crashed clients
 * - On timeout: Cleanup session, return to LISTENING
 *
 * Example Server Flow:
 * 1. LISTENING: Wait for START packet
 * 2. START received → SESSION_INIT
 * 3. SESSION_INIT: Send START-ACK → RECEIVING_DATA
 * 4. RECEIVING_DATA: Wait for DATA packet
 * 5. DATA received → Decrypt, write → DATA_ACK_SENT
 * 6. DATA_ACK_SENT: Send DATA-ACK → RECEIVING_DATA
 * 7. Repeat steps 4-6 until END received
 * 8. SESSION_CLOSING: Send END-ACK → COMPLETED
 * 9. COMPLETED: Cleanup → LISTENING (ready for next transfer)
 *
 * RETRY LOGIC:
 * Client implements exponential backoff for retries:
 * - Attempt 1: 2s timeout
 * - Attempt 2: 4s timeout
 * - Attempt 3: 8s timeout
 * - Attempt 4: 10s timeout (capped at MAX_TIMEOUT)
 * - Attempt 5: 10s timeout
 * - After 5 attempts: Give up, transition to ERROR
 *
 * STATE VALIDATION:
 * - client_sm_is_valid_transition(): Validates state transitions
 * - Prevents invalid transitions (e.g., IDLE → COMPLETED)
 * - Ensures protocol correctness
 * - Returns 1 if transition is valid, 0 otherwise
 *
 * ERROR RECOVERY:
 * - client_sm_can_recover(): Checks if recovery is possible
 * - Recoverable states: START_SENT, DATA_WAIT_ACK, END_SENT (have retry logic)
 * - Non-recoverable: CLIENT_ABORT, CLIENT_COMPLETED (terminal states)
 * - server_sm_can_recover(): Always returns 1 (server can return to LISTENING)
 *
 * STATISTICS:
 * - client_sm_get_statistics(): Returns formatted string with:
 *   - Current state, session ID, sequence number
 *   - Retry count, timeout value
 *   - Time in current state, total elapsed time
 * - server_sm_get_statistics(): Returns formatted string with:
 *   - Current state, session ID, expected sequence
 *   - Last ACK sent, idle time
 *
 * IMPLEMENTATION NOTES:
 * - State machines are NOT thread-safe (use per-thread instances)
 * - Transitions logged to stdout for debugging
 * - Timeout checking is polling-based (not timer-interrupt)
 * - State entry time recorded on every transition
 * - Last activity time updated on packet send/receive
 * ==================================================================================
 */

#include "secret.h"
#include "state_machine.h"

static const char *client_state_names[] = {
    "IDLE", "START_SENT", "DATA_TRANSFER", "DATA_WAIT_ACK",
    "END_SENT", "COMPLETED", "ERROR", "ABORT"
};

static const char *server_state_names[] = {
    "LISTENING", "SESSION_INIT", "RECEIVING_DATA", "DATA_ACK_SENT",
    "SESSION_CLOSING", "COMPLETED", "ERROR", "TIMEOUT"
};

static const char *event_names[] = {
    "TIMEOUT", "PACKET_RECEIVED", "PACKET_SENT", "ACK_RECEIVED",
    "ERROR", "FILE_EOF", "USER_ABORT", "RETRY_EXCEEDED"
};

const char *client_state_name(client_state_t state) {
    if (state >= sizeof(client_state_names) / sizeof(client_state_names[0])) {
        return "UNKNOWN";
    }
    return client_state_names[state];
}

const char *server_state_name(server_state_t state) {
    if (state >= sizeof(server_state_names) / sizeof(server_state_names[0])) {
        return "UNKNOWN";
    }
    return server_state_names[state];
}

const char *event_name(protocol_event_t event) {
    if (event >= sizeof(event_names) / sizeof(event_names[0])) {
        return "UNKNOWN";
    }
    return event_names[event];
}

void client_sm_init(client_state_machine_t *sm, uint16_t session_id) {
    if (!sm) {
        ERROR_SET(ERR_INVALID_ARGS, "State machine cannot be NULL");
        return;
    }

    memset(sm, 0, sizeof(client_state_machine_t));
    sm->current_state = CLIENT_IDLE;
    sm->previous_state = CLIENT_IDLE;
    sm->timeout_seconds = INITIAL_TIMEOUT;
    sm->state_entry_time = time(NULL);
    sm->last_activity = sm->state_entry_time;
    sm->session_id = session_id;
    sm->current_seq = 0;  // Will be set to 1 after START ACK
    sm->ack_expected = 0;
    sm->retry_count = 0;
}

void server_sm_init(server_state_machine_t *sm) {
    if (!sm) {
        ERROR_SET(ERR_INVALID_ARGS, "State machine cannot be NULL");
        return;
    }

    memset(sm, 0, sizeof(server_state_machine_t));
    sm->current_state = SERVER_LISTENING;
    sm->previous_state = SERVER_LISTENING;
    sm->idle_timeout = 60;
    sm->state_entry_time = time(NULL);
    sm->last_activity = sm->state_entry_time;
    sm->expected_seq = 0;
    sm->last_ack_sent = 0;
    sm->session_id = 0;
}

int client_sm_transition(client_state_machine_t *sm, client_state_t new_state, const char *reason) {
    ERROR_CHECK(sm != NULL, ERR_INVALID_ARGS, "State machine cannot be NULL");
    ERROR_CHECK(reason != NULL, ERR_INVALID_ARGS, "Transition reason cannot be NULL");

    /* Validate state transition */
    if (!client_sm_is_valid_transition(sm->current_state, new_state)) {
        ERROR_SET(ERR_STATE_MACHINE, "Invalid state transition attempted");
        return -1;
    }

    sm->previous_state = sm->current_state;
    sm->current_state = new_state;
    sm->state_entry_time = time(NULL);
    sm->last_activity = sm->state_entry_time;

    /* Handle timeout adjustments */
    if (new_state == CLIENT_ERROR || new_state == CLIENT_ABORT) {
        sm->timeout_seconds = INITIAL_TIMEOUT;  /* Reset for next attempt */
    }

    /* Reset retry count for successful progressions */
    if (new_state != CLIENT_ERROR && new_state != CLIENT_ABORT &&
        new_state != CLIENT_START_SENT && new_state != CLIENT_DATA_WAIT_ACK &&
        new_state != CLIENT_END_SENT) {
        sm->retry_count = 0;
    }

    return 0;
}

int server_sm_transition(server_state_machine_t *sm, server_state_t new_state, const char *reason) {
    if (!sm || !reason) return -1;

    printf("[SERVER-SM] Transition: %s -> %s (reason: %s)\n",
           server_state_name(sm->current_state),
           server_state_name(new_state),
           reason);

    sm->previous_state = sm->current_state;
    sm->current_state = new_state;
    sm->state_entry_time = time(NULL);
    sm->last_activity = sm->state_entry_time;

    return 0;
}

int client_sm_check_timeout(client_state_machine_t *sm) {
    if (!sm) return 0;

    time_t now = time(NULL);
    if (now - sm->state_entry_time > sm->timeout_seconds) {
        return 1;
    }
    return 0;
}

int server_sm_check_timeout(server_state_machine_t *sm) {
    if (!sm) return 0;

    time_t now = time(NULL);
    if (now - sm->last_activity > sm->idle_timeout) {
        return 1;
    }
    return 0;
}

int client_sm_handle_event(client_state_machine_t *sm, protocol_event_t event, void *event_data) {
    (void)event_data;  /* Unused parameter */
    if (!sm) return -1;

    printf("[CLIENT-SM] Handling event %s in state %s\n",
           event_name(event), client_state_name(sm->current_state));

    switch (sm->current_state) {
        case CLIENT_IDLE:
            if (event == EVENT_PACKET_SENT) {
                return client_sm_transition(sm, CLIENT_START_SENT, "START packet sent");
            }
            break;

        case CLIENT_START_SENT:
            if (event == EVENT_ACK_RECEIVED) {
                // START packet has seq=0, data starts at seq=1
                return client_sm_transition(sm, CLIENT_DATA_TRANSFER, "START ACK received");
            } else if (event == EVENT_TIMEOUT) {
                sm->retry_count++;
                if (sm->retry_count >= MAX_RETRIES) {
                    return client_sm_transition(sm, CLIENT_ERROR, "Max retries exceeded");
                }
                sm->timeout_seconds = (sm->timeout_seconds < MAX_TIMEOUT) ?
                                    sm->timeout_seconds * 2 : MAX_TIMEOUT;
                sm->state_entry_time = time(NULL);
                printf("[CLIENT-SM] Timeout in START_SENT, retry %d/%d\n",
                       sm->retry_count, MAX_RETRIES);
            }
            break;

        case CLIENT_DATA_TRANSFER:
            if (event == EVENT_PACKET_SENT) {
                // Don't increment seq here - wait for ACK
                return client_sm_transition(sm, CLIENT_DATA_WAIT_ACK, "DATA packet sent");
            } else if (event == EVENT_FILE_EOF) {
                // Keep same sequence for END packet
                return 0; // Stay in same state, will transition after END sent
            }
            break;

        case CLIENT_DATA_WAIT_ACK:
            if (event == EVENT_ACK_RECEIVED) {
                sm->timeout_seconds = INITIAL_TIMEOUT;
                sm->retry_count = 0;
                // Sequence increment handled in client_engine.c after ACK
                return client_sm_transition(sm, CLIENT_DATA_TRANSFER, "DATA ACK received");
            } else if (event == EVENT_TIMEOUT) {
                sm->retry_count++;
                if (sm->retry_count >= MAX_RETRIES) {
                    return client_sm_transition(sm, CLIENT_ERROR, "Max retries exceeded");
                }
                sm->timeout_seconds = (sm->timeout_seconds < MAX_TIMEOUT) ?
                                    sm->timeout_seconds * 2 : MAX_TIMEOUT;
                // Go back to DATA_TRANSFER to retransmit
                return client_sm_transition(sm, CLIENT_DATA_TRANSFER, "Timeout - retransmit");
            }
            break;

        case CLIENT_END_SENT:
            if (event == EVENT_ACK_RECEIVED) {
                return client_sm_transition(sm, CLIENT_COMPLETED, "END ACK received");
            } else if (event == EVENT_TIMEOUT) {
                sm->retry_count++;
                if (sm->retry_count >= MAX_RETRIES) {
                    return client_sm_transition(sm, CLIENT_ERROR, "Max retries exceeded");
                }
                sm->timeout_seconds = (sm->timeout_seconds < MAX_TIMEOUT) ?
                                    sm->timeout_seconds * 2 : MAX_TIMEOUT;
                sm->state_entry_time = time(NULL);
                printf("[CLIENT-SM] Timeout in END_SENT, retry %d/%d\n",
                       sm->retry_count, MAX_RETRIES);
            }
            break;

        case CLIENT_COMPLETED:
        case CLIENT_ERROR:
        case CLIENT_ABORT:
            break;
    }

    return 0;
}

int server_sm_handle_event(server_state_machine_t *sm, protocol_event_t event, void *event_data) {
    if (!sm) return -1;

    printf("[SERVER-SM] Handling event %s in state %s\n",
           event_name(event), server_state_name(sm->current_state));

    switch (sm->current_state) {
        case SERVER_LISTENING:
            if (event == EVENT_PACKET_RECEIVED) {
                iftp_header_t *header = (iftp_header_t *)event_data;
                if (header && header->msg_type == IFTP_START) {
                    sm->session_id = header->session_id;
                    sm->expected_seq = 1;  // First DATA packet will have seq=1
                    sm->last_ack_sent = 0; // START ACK has seq=0
                    return server_sm_transition(sm, SERVER_SESSION_INIT, "START packet received");
                }
            }
            break;

        case SERVER_SESSION_INIT:
            if (event == EVENT_PACKET_SENT) {
                return server_sm_transition(sm, SERVER_RECEIVING_DATA, "START ACK sent");
            }
            break;

        case SERVER_RECEIVING_DATA:
            if (event == EVENT_PACKET_RECEIVED) {
                iftp_header_t *header = (iftp_header_t *)event_data;
                if (header) {
                    if (header->msg_type == IFTP_DATA) {
                        if (header->seq_num == sm->expected_seq) {
                            sm->last_ack_sent = header->seq_num;
                            return server_sm_transition(sm, SERVER_DATA_ACK_SENT, "DATA packet received");
                        } else if (header->seq_num < sm->expected_seq) {
                            // Duplicate packet, re-send ACK
                            printf("[SERVER-SM] Duplicate packet: seq %u (expected %u)\n",
                                   header->seq_num, sm->expected_seq);
                        } else {
                            printf("[SERVER-SM] Out-of-order packet: expected %u, got %u\n",
                                   sm->expected_seq, header->seq_num);
                        }
                    } else if (header->msg_type == IFTP_END) {
                        // END packet can have the last seq number
                        sm->last_ack_sent = header->seq_num;
                        return server_sm_transition(sm, SERVER_SESSION_CLOSING, "END packet received");
                    }
                }
            } else if (event == EVENT_TIMEOUT) {
                return server_sm_transition(sm, SERVER_TIMEOUT, "Idle timeout");
            }
            break;

        case SERVER_DATA_ACK_SENT:
            if (event == EVENT_PACKET_SENT) {
                sm->expected_seq++;
                return server_sm_transition(sm, SERVER_RECEIVING_DATA, "DATA ACK sent");
            }
            break;

        case SERVER_SESSION_CLOSING:
            if (event == EVENT_PACKET_SENT) {
                return server_sm_transition(sm, SERVER_COMPLETED, "Final ACK sent");
            } else if (event == EVENT_PACKET_RECEIVED) {
                // Handle retransmitted END packets
                iftp_header_t *header = (iftp_header_t *)event_data;
                if (header && header->msg_type == IFTP_END) {
                    printf("[SERVER-SM] Retransmitted END packet received\n");
                    // Stay in same state, will re-send ACK
                }
            }
            break;

        case SERVER_COMPLETED:
            if (event == EVENT_TIMEOUT) {
                return server_sm_transition(sm, SERVER_LISTENING, "Session reset");
            }
            break;

        case SERVER_ERROR:
        case SERVER_TIMEOUT:
            if (event == EVENT_TIMEOUT) {
                return server_sm_transition(sm, SERVER_LISTENING, "Reset after error");
            }
            break;
    }

    return 0;
}

int client_sm_is_valid_transition(client_state_t current, client_state_t next) {
    /* Define valid state transitions for the client state machine */
    switch (current) {
        case CLIENT_IDLE:
            return (next == CLIENT_START_SENT || next == CLIENT_ERROR || next == CLIENT_ABORT);

        case CLIENT_START_SENT:
            return (next == CLIENT_DATA_TRANSFER || next == CLIENT_START_SENT ||
                   next == CLIENT_ERROR || next == CLIENT_ABORT);

        case CLIENT_DATA_TRANSFER:
            return (next == CLIENT_DATA_WAIT_ACK || next == CLIENT_END_SENT ||
                   next == CLIENT_ERROR || next == CLIENT_ABORT);

        case CLIENT_DATA_WAIT_ACK:
            return (next == CLIENT_DATA_TRANSFER || next == CLIENT_DATA_WAIT_ACK ||
                   next == CLIENT_END_SENT || next == CLIENT_ERROR || next == CLIENT_ABORT);

        case CLIENT_END_SENT:
            return (next == CLIENT_COMPLETED || next == CLIENT_END_SENT ||
                   next == CLIENT_ERROR || next == CLIENT_ABORT);

        case CLIENT_COMPLETED:
        case CLIENT_ERROR:
        case CLIENT_ABORT:
            return 1;  /* Terminal states can transition to any state for restart */

        default:
            return 0;
    }
}

int server_sm_is_valid_transition(server_state_t current, server_state_t next) {
    /* Define valid state transitions for the server state machine */
    switch (current) {
        case SERVER_LISTENING:
            return (next == SERVER_SESSION_INIT || next == SERVER_ERROR);

        case SERVER_SESSION_INIT:
            return (next == SERVER_RECEIVING_DATA || next == SERVER_ERROR ||
                   next == SERVER_TIMEOUT || next == SERVER_LISTENING);

        case SERVER_RECEIVING_DATA:
            return (next == SERVER_DATA_ACK_SENT || next == SERVER_SESSION_CLOSING ||
                   next == SERVER_ERROR || next == SERVER_TIMEOUT);

        case SERVER_DATA_ACK_SENT:
            return (next == SERVER_RECEIVING_DATA || next == SERVER_ERROR ||
                   next == SERVER_TIMEOUT);

        case SERVER_SESSION_CLOSING:
            return (next == SERVER_COMPLETED || next == SERVER_ERROR ||
                   next == SERVER_TIMEOUT || next == SERVER_SESSION_CLOSING);

        case SERVER_COMPLETED:
        case SERVER_ERROR:
        case SERVER_TIMEOUT:
            return (next == SERVER_LISTENING);  /* Return to listening */

        default:
            return 0;
    }
}

int client_sm_can_recover(const client_state_machine_t *sm) {
    ERROR_CHECK(sm != NULL, ERR_INVALID_ARGS, "State machine cannot be NULL");

    /* Check if client can recover from current state */
    if (sm->retry_count >= MAX_RETRIES) {
        return 0;  /* Too many retries */
    }

    switch (sm->current_state) {
        case CLIENT_START_SENT:
        case CLIENT_DATA_WAIT_ACK:
        case CLIENT_END_SENT:
            return 1;  /* These states support retry */

        case CLIENT_ERROR:
            return (sm->retry_count < MAX_RETRIES - 1);

        case CLIENT_ABORT:
        case CLIENT_COMPLETED:
            return 0;  /* Terminal states */

        default:
            return 1;  /* Other states can generally continue */
    }
}

int server_sm_can_recover(const server_state_machine_t *sm) {
    ERROR_CHECK(sm != NULL, ERR_INVALID_ARGS, "State machine cannot be NULL");

    /* Server can usually recover by returning to listening state */
    switch (sm->current_state) {
        case SERVER_COMPLETED:
            return 1;  /* Always can return to listening */

        case SERVER_ERROR:
        case SERVER_TIMEOUT:
            return 1;  /* Can recover to listening */

        default:
            return 1;  /* Generally recoverable */
    }
}

void client_sm_reset(client_state_machine_t *sm) {
    if (!sm) return;


    uint16_t session_id = sm->session_id;  /* Preserve session ID */
    client_sm_init(sm, session_id);
}

void server_sm_reset(server_state_machine_t *sm) {
    if (!sm) return;


    server_sm_init(sm);
}

int client_sm_handle_timeout_with_backoff(client_state_machine_t *sm) {
    ERROR_CHECK(sm != NULL, ERR_INVALID_ARGS, "State machine cannot be NULL");

    sm->retry_count++;

    if (sm->retry_count >= MAX_RETRIES) {
        return client_sm_transition(sm, CLIENT_ERROR, "Maximum retries exceeded");
    }

    /* Exponential backoff with jitter */
    int new_timeout = sm->timeout_seconds * 2;
    if (new_timeout > MAX_TIMEOUT) {
        new_timeout = MAX_TIMEOUT;
    }

    /* Add jitter (up to 25% of timeout) */
    int jitter = rand() % (new_timeout / 4 + 1);
    new_timeout += jitter;

    sm->timeout_seconds = new_timeout;
    sm->state_entry_time = time(NULL);

    return 0;  /* Remain in current state for retry */
}

int client_sm_get_statistics(const client_state_machine_t *sm, char *buffer, size_t buffer_size) {
    ERROR_CHECK(sm != NULL, ERR_INVALID_ARGS, "State machine cannot be NULL");
    ERROR_CHECK(buffer != NULL, ERR_INVALID_ARGS, "Buffer cannot be NULL");

    time_t now = time(NULL);
    int elapsed = (int)(now - sm->state_entry_time);
    int total_elapsed = (int)(now - sm->last_activity);

    return snprintf(buffer, buffer_size,
                   "Session %u: State=%s, Seq=%u, Retries=%u/%u, "
                   "StateTime=%ds, TotalTime=%ds, Timeout=%ds",
                   sm->session_id, client_state_name(sm->current_state),
                   sm->current_seq, sm->retry_count, MAX_RETRIES,
                   elapsed, total_elapsed, sm->timeout_seconds);
}

int server_sm_get_statistics(const server_state_machine_t *sm, char *buffer, size_t buffer_size) {
    ERROR_CHECK(sm != NULL, ERR_INVALID_ARGS, "State machine cannot be NULL");
    ERROR_CHECK(buffer != NULL, ERR_INVALID_ARGS, "Buffer cannot be NULL");

    time_t now = time(NULL);
    int elapsed = (int)(now - sm->state_entry_time);
    int idle_time = (int)(now - sm->last_activity);

    return snprintf(buffer, buffer_size,
                   "Session %u: State=%s, ExpectedSeq=%u, LastAck=%u, "
                   "StateTime=%ds, IdleTime=%ds, IdleTimeout=%ds",
                   sm->session_id, server_state_name(sm->current_state),
                   sm->expected_seq, sm->last_ack_sent,
                   elapsed, idle_time, sm->idle_timeout);
}