// =============================================================================
//  station/station_mgr.cpp -- Control authority state machine
// =============================================================================

#include "station_mgr.h"

#include <Arduino.h>
#include <cstring>
#include <cstdio>

namespace arconcentrador::station {

namespace {

uint8_t  g_commander       = STATION_NONE;
uint8_t  g_pending_request = STATION_NONE;  // station waiting for REQCMD ack
uint32_t g_request_time_ms = 0;
static constexpr uint32_t REQCMD_TIMEOUT_MS = 10000;

// --- NMEA checksum ---
static uint8_t nmea_crc(const char* s) {
    uint8_t crc = 0;
    for (; *s; ++s) crc ^= (uint8_t)*s;
    return crc;
}

static void make_sentence(char* out, size_t len, const char* body) {
    char tmp[128];
    snprintf(tmp, sizeof(tmp), "PARP,%s", body);
    const uint8_t crc = nmea_crc(tmp);
    snprintf(out, len, "$PARP,%s*%02X\r\n", body, crc);
}

static void transfer_to(uint8_t new_commander,
                        char* out, size_t out_len) {
    const uint8_t prev = g_commander;
    g_commander        = new_commander;
    g_pending_request  = STATION_NONE;
    char body[64];
    snprintf(body, sizeof(body), "CMDTRANSFER,%02d,%02d", prev, new_commander);
    make_sentence(out, out_len, body);
    Serial.printf("[STATION] mando transferido %d → %d\n", prev, new_commander);
}

}  // namespace

void station_init() {
    g_commander      = STATION_NONE;
    g_pending_request = STATION_NONE;
}

uint8_t current_commander() {
    return g_commander;
}

void station_process(const char* cmd, uint8_t station_id,
                     char* out_broadcast, size_t out_len) {
    out_broadcast[0] = '\0';

    // --- TAKECMD: bridge override, always immediate ---
    if (strcmp(cmd, "TAKECMD") == 0 && station_id == STATION_BRIDGE) {
        transfer_to(STATION_BRIDGE, out_broadcast, out_len);
        return;
    }

    // --- REQCMD: request command from another station ---
    if (strcmp(cmd, "REQCMD") == 0) {
        if (station_id == g_commander) return;  // already the commander
        g_pending_request = station_id;
        g_request_time_ms = millis();
        char body[64];
        snprintf(body, sizeof(body), "CMDREQUEST,%02d", station_id);
        make_sentence(out_broadcast, out_len, body);
        Serial.printf("[STATION] estacion %d solicita el mando\n", station_id);
        return;
    }

    // --- RELCMD: commander voluntarily releases ---
    if (strcmp(cmd, "RELCMD") == 0 && station_id == g_commander) {
        if (g_pending_request != STATION_NONE) {
            transfer_to(g_pending_request, out_broadcast, out_len);
        } else {
            transfer_to(STATION_NONE, out_broadcast, out_len);
        }
        return;
    }

    // --- ACKCMD: commander confirms the pending request ---
    if (strcmp(cmd, "ACKCMD") == 0 && station_id == g_commander) {
        if (g_pending_request != STATION_NONE) {
            transfer_to(g_pending_request, out_broadcast, out_len);
        }
        return;
    }

    // --- DENYCMD: commander denies the pending request ---
    if (strcmp(cmd, "DENYCMD") == 0 && station_id == g_commander) {
        g_pending_request = STATION_NONE;
        char body[64];
        snprintf(body, sizeof(body), "CMDDENIED,%02d", station_id);
        make_sentence(out_broadcast, out_len, body);
        Serial.printf("[STATION] solicitud de estacion %d denegada\n",
                      g_pending_request);
        return;
    }
}

void station_tick() {
    if (g_pending_request == STATION_NONE) return;
    if (millis() - g_request_time_ms >= REQCMD_TIMEOUT_MS) {
        // Auto-transfer after 10 s with no response from commander.
        Serial.printf("[STATION] timeout REQCMD → auto-transfer a estacion %d\n",
                      g_pending_request);
        // We can't write out_broadcast here (no output buffer in tick),
        // so the main loop re-broadcasts the transfer status on the next
        // regular STATUS pulse.
        g_commander       = g_pending_request;
        g_pending_request = STATION_NONE;
    }
}

}  // namespace arconcentrador::station
