#include <Arduino.h>

#include "app_context.h"
#include "config.h"
#include "data_model.h"
#include "dd3_legacy_core.h"
#include "display_ui.h"
#include "lora_transport.h"
#include "mqtt_client.h"
#include "payload_codec.h"
#include "power_manager.h"
#include "receiver_pipeline.h"
#include "sd_logger.h"
#include "sender_state_machine.h"
#include "time_manager.h"
#include "web_server.h"
#include "wifi_manager.h"

#include <stdarg.h>

#ifdef ARDUINO_ARCH_ESP32
#include <esp_system.h>
#include <esp_task_wdt.h>
#endif

static DeviceRole g_role = DeviceRole::Sender;
static uint16_t g_short_id = 0;
static char g_device_id[16] = "";

static WifiMqttConfig g_cfg;
static ReceiverSharedState g_receiver_shared = {};
static SenderStateMachine g_sender_state_machine;
static ReceiverPipeline g_receiver_pipeline;

static void serial_debug_printf(const char *fmt, ...) {
  if (!SERIAL_DEBUG_MODE) {
    return;
  }
  char buf[256];
  va_list args;
  va_start(args, fmt);
  vsnprintf(buf, sizeof(buf), fmt, args);
  va_end(args);
  Serial.println(buf);
}

#ifdef ARDUINO_ARCH_ESP32
static void watchdog_init() {
  esp_task_wdt_deinit();
  esp_task_wdt_config_t config = {};
  config.timeout_ms = WATCHDOG_TIMEOUT_SEC * 1000;
  config.idle_core_mask = 0;
  config.trigger_panic = true;
  esp_task_wdt_init(&config);
  esp_task_wdt_add(nullptr);
}

static void watchdog_kick() {
  esp_task_wdt_reset();
}
#else
static void watchdog_init() {}
static void watchdog_kick() {}
#endif

void setup() {
  Serial.begin(115200);
  delay(200);
  dd3_legacy_core_force_link();

#ifdef PAYLOAD_CODEC_TEST
  payload_codec_self_test();
#endif

  watchdog_init();
  g_role = detect_role();
  init_device_ids(g_short_id, g_device_id, sizeof(g_device_id));

  display_set_role(g_role);
  if (SERIAL_DEBUG_MODE) {
#ifdef ARDUINO_ARCH_ESP32
    serial_debug_printf("boot: reset_reason=%d", static_cast<int>(esp_reset_reason()));
#endif
    serial_debug_printf("boot: role=%s short_id=%04X dev=%s", g_role == DeviceRole::Sender ? "sender" : "receiver",
                        g_short_id, g_device_id);
  }

  lora_init();
  display_init();
  display_set_self_ids(g_short_id, g_device_id);

  if (g_role == DeviceRole::Sender) {
    SenderStateMachineConfig sender_cfg = {};
    sender_cfg.short_id = g_short_id;
    sender_cfg.device_id = g_device_id;
    g_sender_state_machine.begin(sender_cfg);
    return;
  }

  power_receiver_init();
  lora_receive_continuous();
  pinMode(PIN_ROLE, INPUT); // release pulldown before SD uses GPIO14 as SCK
  sd_logger_init();
  wifi_manager_init();

  ReceiverPipelineConfig receiver_cfg = {};
  receiver_cfg.short_id = g_short_id;
  receiver_cfg.device_id = g_device_id;
  receiver_cfg.shared = &g_receiver_shared;
  g_receiver_pipeline.begin(receiver_cfg);

  display_set_sender_statuses(g_receiver_shared.sender_statuses, NUM_SENDERS);

  bool has_cfg = wifi_load_config(g_cfg);
  
  // Store WiFi config in shared state for later reconnection attempts
  g_receiver_shared.wifi_config = g_cfg;
  g_receiver_shared.last_wifi_reconnect_attempt_ms = 0;
  
  if (has_cfg && wifi_connect_sta(g_cfg)) {
    g_receiver_shared.ap_mode = false;
    time_receiver_init(g_cfg.ntp_server_1.c_str(), g_cfg.ntp_server_2.c_str());
    mqtt_init(g_cfg, g_device_id);
    web_server_set_config(g_cfg);
    web_server_set_sender_faults(g_receiver_shared.sender_faults_remote, g_receiver_shared.sender_last_error_remote);
    web_server_begin_sta(g_receiver_shared.sender_statuses, NUM_SENDERS);
  } else {
    g_receiver_shared.ap_mode = true;
    char ap_ssid[32];
    snprintf(ap_ssid, sizeof(ap_ssid), "%s%04X", AP_SSID_PREFIX, g_short_id);
    wifi_start_ap(ap_ssid, AP_PASSWORD);
    
    // Store AP credentials in shared state for potential reconnection fallback
    strncpy(g_receiver_shared.ap_ssid, ap_ssid, sizeof(g_receiver_shared.ap_ssid) - 1);
    g_receiver_shared.ap_ssid[sizeof(g_receiver_shared.ap_ssid) - 1] = '\0';
    strncpy(g_receiver_shared.ap_password, AP_PASSWORD, sizeof(g_receiver_shared.ap_password) - 1);
    g_receiver_shared.ap_password[sizeof(g_receiver_shared.ap_password) - 1] = '\0';
    
    if (g_cfg.ntp_server_1.isEmpty()) {
      g_cfg.ntp_server_1 = "pool.ntp.org";
    }
    if (g_cfg.ntp_server_2.isEmpty()) {
      g_cfg.ntp_server_2 = "time.nist.gov";
    }
    web_server_set_config(g_cfg);
    web_server_set_sender_faults(g_receiver_shared.sender_faults_remote, g_receiver_shared.sender_last_error_remote);
    web_server_begin_ap(g_receiver_shared.sender_statuses, NUM_SENDERS);
  }
}

void loop() {
  if (g_role == DeviceRole::Sender) {
    g_sender_state_machine.loop();
  } else {
    g_receiver_pipeline.loop();
  }
}
