#include "display_ui.h"
#include "config.h"
#include "time_manager.h"
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <time.h>

static Adafruit_SSD1306 display(OLED_WIDTH, OLED_HEIGHT, &Wire, -1);

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

static MeterData g_last_meter = {};
static bool g_last_read_ok = false;
static bool g_last_tx_ok = false;
static uint32_t g_last_read_ts = 0;
static uint32_t g_last_tx_ts = 0;
static uint32_t g_last_read_ms = 0;
static uint32_t g_last_tx_ms = 0;
static uint8_t g_sender_queue_depth = 0;
static bool g_sender_build_pending = false;
static uint16_t g_sender_last_acked_batch_id = 0;
static uint16_t g_sender_current_batch_id = 0;
static FaultType g_last_error = FaultType::None;
static uint32_t g_last_error_ts = 0;
static uint32_t g_last_error_ms = 0;

static const SenderStatus *g_statuses = nullptr;
static uint8_t g_status_count = 0;

static bool g_ap_mode = false;
static String g_wifi_ssid;
static bool g_mqtt_ok = false;

static bool g_oled_on = true;
static bool g_prev_ctrl_high = false;
static uint32_t g_last_page_ms = 0;
static uint8_t g_page = 0;
static uint32_t g_last_activity_ms = 0;
static bool g_display_ready = false;
static uint32_t g_last_init_attempt_ms = 0;
static bool g_last_oled_on = true;

#ifdef ENABLE_TEST_MODE
static char g_test_code[8] = "";
static char g_test_codes[NUM_SENDERS][8] = {};
#endif

static void oled_set_power(bool on) {
  if (on) {
    display.ssd1306_command(SSD1306_DISPLAYON);
  } else {
    display.ssd1306_command(SSD1306_DISPLAYOFF);
  }
}

void display_power_down() {
  if (!g_display_ready) {
    return;
  }
  display.clearDisplay();
  display.display();
  oled_set_power(false);
  g_oled_on = false;
  g_last_oled_on = false;
}

void display_init() {
  if (g_role == DeviceRole::Sender) {
    pinMode(PIN_OLED_CTRL, INPUT_PULLDOWN);
  }
  Wire.begin(PIN_OLED_SDA, PIN_OLED_SCL);
  Wire.setClock(100000);
  g_display_ready = display.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR);
  if (g_display_ready) {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.ssd1306_command(SSD1306_DISPLAYON);
    display.display();
  }
  g_last_init_attempt_ms = millis();
  g_last_activity_ms = millis();
}

void display_set_role(DeviceRole role) {
  g_role = role;
}

void display_set_self_ids(uint16_t short_id, const char *device_id) {
  g_short_id = short_id;
  strncpy(g_device_id, device_id, sizeof(g_device_id));
  g_device_id[sizeof(g_device_id) - 1] = '\0';
}

void display_set_sender_statuses(const SenderStatus *statuses, uint8_t count) {
  g_statuses = statuses;
  g_status_count = count;
}

void display_set_last_meter(const MeterData &data) {
  g_last_meter = data;
}

void display_set_last_read(bool ok, uint32_t ts_utc) {
  g_last_read_ok = ok;
  g_last_read_ts = ts_utc;
  g_last_read_ms = millis();
}

void display_set_last_tx(bool ok, uint32_t ts_utc) {
  g_last_tx_ok = ok;
  g_last_tx_ts = ts_utc;
  g_last_tx_ms = millis();
}

void display_set_sender_queue(uint8_t depth, bool build_pending) {
  g_sender_queue_depth = depth;
  g_sender_build_pending = build_pending;
}

void display_set_sender_batches(uint16_t last_acked_batch_id, uint16_t current_batch_id) {
  g_sender_last_acked_batch_id = last_acked_batch_id;
  g_sender_current_batch_id = current_batch_id;
}

void display_set_last_error(FaultType type, uint32_t ts_utc, uint32_t ts_ms) {
  g_last_error = type;
  g_last_error_ts = ts_utc;
  g_last_error_ms = ts_ms;
}

void display_set_receiver_status(bool ap_mode, const char *ssid, bool mqtt_ok) {
  g_ap_mode = ap_mode;
  g_wifi_ssid = ssid ? ssid : "";
  g_mqtt_ok = mqtt_ok;
}

#ifdef ENABLE_TEST_MODE
void display_set_test_code(const char *code) {
  strncpy(g_test_code, code, sizeof(g_test_code));
  g_test_code[sizeof(g_test_code) - 1] = '\0';
}

void display_set_test_code_for_sender(uint8_t index, const char *code) {
  if (index >= NUM_SENDERS) {
    return;
  }
  strncpy(g_test_codes[index], code, sizeof(g_test_codes[index]));
  g_test_codes[index][sizeof(g_test_codes[index]) - 1] = '\0';
}
#endif

static uint32_t age_seconds(uint32_t ts_utc, uint32_t ts_ms) {
  if (time_is_synced() && ts_utc > 0) {
    uint32_t now = time_get_utc();
    return now > ts_utc ? now - ts_utc : 0;
  }
  return (millis() - ts_ms) / 1000;
}

static bool render_last_error_line(uint8_t y) {
  if (g_last_error == FaultType::None) {
    return false;
  }
  const char *label = "unk";
  if (g_last_error == FaultType::MeterRead) {
    label = "meter";
  } else if (g_last_error == FaultType::Decode) {
    label = "decode";
  } else if (g_last_error == FaultType::LoraTx) {
    label = "lora";
  } else if (g_last_error == FaultType::TimeSync) {
    label = "timesync";
  }
  display.setCursor(0, y);
  display.printf("Err: %s %lus", label, static_cast<unsigned long>(age_seconds(g_last_error_ts, g_last_error_ms)));
  return true;
}

static void render_last_sync_line(uint8_t y, bool include_time) {
  display.setCursor(0, y);
  uint32_t last_sync = time_get_last_sync_utc();
  if (last_sync == 0 || !time_is_synced()) {
    display.print("Sync: --");
    return;
  }
  uint32_t age = time_get_last_sync_age_sec();
  if (include_time) {
    time_t t = last_sync;
    struct tm timeinfo;
    localtime_r(&t, &timeinfo);
    display.printf("Sync: %lus %02d:%02d", static_cast<unsigned long>(age), timeinfo.tm_hour, timeinfo.tm_min);
  } else {
    display.printf("Sync: %lus ago", static_cast<unsigned long>(age));
  }
}

static void render_sender_status() {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.printf("SENDER %s", g_device_id);

  char time_buf[8];
  time_get_local_hhmm(time_buf, sizeof(time_buf));
  display.setCursor(0, 12);
  display.printf("%s %.2fV %u%%", time_buf, g_last_meter.battery_voltage_v, g_last_meter.battery_percent);

  display.setCursor(0, 24);
  display.printf("Read %s %lus ago", g_last_read_ok ? "OK" : "ERR", static_cast<unsigned long>(age_seconds(g_last_read_ts, g_last_read_ms)));

  display.setCursor(0, 36);
  display.printf("TX %s %lus Q%u%s A%u C%u",
                 g_last_tx_ok ? "OK" : "ERR",
                 static_cast<unsigned long>(age_seconds(g_last_tx_ts, g_last_tx_ms)),
                 g_sender_queue_depth,
                 g_sender_build_pending ? "+" : "",
                 g_sender_last_acked_batch_id,
                 g_sender_current_batch_id);

#ifdef ENABLE_TEST_MODE
  if (strlen(g_test_code) > 0) {
    display.setCursor(0, 48);
    display.printf("Test %s", g_test_code);
  } else
#endif
  {
    if (!render_last_error_line(48)) {
      render_last_sync_line(48, true);
    }
  }

  display.display();
}

static void render_sender_measurement() {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.printf("E %.1f kWh", g_last_meter.energy_total_kwh);
  display.setCursor(0, 12);
  display.printf("P %.0fW", g_last_meter.total_power_w);
  display.setCursor(0, 24);
  display.printf("L1 %.0fW", g_last_meter.phase_power_w[0]);
  display.setCursor(0, 36);
  display.printf("L2 %.0fW", g_last_meter.phase_power_w[1]);
  display.setCursor(0, 48);
  display.printf("L3 %.0fW", g_last_meter.phase_power_w[2]);
  display.display();
}

static void render_receiver_status() {
  display.clearDisplay();
  display.setCursor(0, 0);
  display.printf("RECEIVER %s", g_device_id);

  display.setCursor(0, 12);
  if (g_ap_mode) {
    display.print("WiFi: AP");
  } else {
    display.printf("WiFi: %s", g_wifi_ssid.c_str());
  }

  display.setCursor(0, 24);
  display.printf("MQTT: %s", g_mqtt_ok ? "OK" : "RETRY");

  char time_buf[8];
  time_get_local_hhmm(time_buf, sizeof(time_buf));
  display.setCursor(0, 36);
  display.printf("Time: %s", time_buf);

  uint32_t latest = 0;
  if (g_statuses) {
    for (uint8_t i = 0; i < g_status_count; ++i) {
      if (g_statuses[i].has_data && g_statuses[i].last_update_ts_utc > latest) {
        latest = g_statuses[i].last_update_ts_utc;
      }
    }
  }

  display.setCursor(0, 48);
  if (latest == 0 || !time_is_synced()) {
    display.print("Upd --:--");
  } else {
    time_t t = latest;
    struct tm timeinfo;
    localtime_r(&t, &timeinfo);
    display.printf("Upd %02d:%02d", timeinfo.tm_hour, timeinfo.tm_min);
  }

  render_last_error_line(56);
  display.display();
}

static void render_receiver_sender(uint8_t index) {
  display.clearDisplay();
  if (!g_statuses || index >= g_status_count) {
    display.setCursor(0, 0);
    display.print("No sender");
    display.display();
    return;
  }

  const SenderStatus &status = g_statuses[index];
  display.setCursor(0, 0);
  uint8_t bat = status.has_data ? status.last_data.battery_percent : 0;
  if (status.has_data) {
    const char *device_id = status.last_data.device_id;
    if (strlen(device_id) >= 4 && strncmp(device_id, "dd3-", 4) == 0) {
      device_id += 4;
    }
    if (status.last_data.link_valid) {
      display.printf("%s R:%d S:%.1f", device_id, status.last_data.link_rssi_dbm, status.last_data.link_snr_db);
    } else {
      display.printf("%s B%u", device_id, bat);
    }
  } else {
    display.printf("%s B--", status.last_data.device_id);
  }

  if (!status.has_data) {
    display.setCursor(0, 12);
    display.print("No data");
    display.display();
    return;
  }

#ifdef ENABLE_TEST_MODE
  if (strlen(g_test_codes[index]) > 0) {
    display.setCursor(0, 12);
    display.printf("Test %s", g_test_codes[index]);
    display.display();
    return;
  }
#endif

  display.setCursor(0, 12);
  display.printf("E %.1f kWh", status.last_data.energy_total_kwh);
  display.setCursor(0, 22);
  display.printf("L1 %.0fW", status.last_data.phase_power_w[0]);
  display.setCursor(0, 32);
  display.printf("L2 %.0fW", status.last_data.phase_power_w[1]);
  display.setCursor(0, 42);
  display.printf("L3 %.0fW", status.last_data.phase_power_w[2]);
  display.setCursor(0, 52);
  display.print("P");
  char p_buf[16];
  snprintf(p_buf, sizeof(p_buf), "%.0fW", status.last_data.total_power_w);
  int16_t x1 = 0;
  int16_t y1 = 0;
  uint16_t w = 0;
  uint16_t h = 0;
  display.getTextBounds(p_buf, 0, 0, &x1, &y1, &w, &h);
  int16_t x = static_cast<int16_t>(display.width() - w);
  if (x < 0) {
    x = 0;
  }
  display.setCursor(x, 52);
  display.print(p_buf);
  display.display();
}

void display_tick() {
  if (!g_display_ready) {
    if (millis() - g_last_init_attempt_ms > 1000) {
      g_last_init_attempt_ms = millis();
      g_display_ready = display.begin(SSD1306_SWITCHCAPVCC, OLED_I2C_ADDR);
      if (g_display_ready) {
        display.clearDisplay();
        display.setTextSize(1);
        display.setTextColor(SSD1306_WHITE);
        display.ssd1306_command(SSD1306_DISPLAYON);
        display.display();
      }
    }
    return;
  }
  bool ctrl_high = false;
  if (g_role == DeviceRole::Sender) {
    ctrl_high = digitalRead(PIN_OLED_CTRL) == HIGH;
  }

  uint32_t now_ms = millis();
  bool ctrl_falling_edge = g_prev_ctrl_high && !ctrl_high;
  if (g_role == DeviceRole::Receiver) {
    g_oled_on = true;
    g_last_activity_ms = now_ms;
  } else {
    if (ctrl_high || ctrl_falling_edge) {
      g_last_activity_ms = now_ms;
    }
    g_oled_on = (now_ms - g_last_activity_ms) < OLED_AUTO_OFF_MS;
  }

  if (g_oled_on) {
    if (!g_last_oled_on) {
      oled_set_power(true);
      g_last_page_ms = millis();
    }
  } else {
    if (g_last_oled_on) {
      display.clearDisplay();
      display.display();
      oled_set_power(false);
    }
    g_last_oled_on = g_oled_on;
    g_prev_ctrl_high = ctrl_high;
    return;
  }
  g_last_oled_on = g_oled_on;

  uint32_t now = millis();
  uint8_t page_count = g_role == DeviceRole::Sender ? 2 : (1 + g_status_count);
  if (page_count == 0) {
    page_count = 1;
  }
  if (now - g_last_page_ms > OLED_PAGE_INTERVAL_MS) {
    g_last_page_ms = now;
    g_page = (g_page + 1) % page_count;
  }

  if (g_role == DeviceRole::Sender) {
    if (g_page == 0) {
      render_sender_status();
    } else {
      render_sender_measurement();
    }
  } else {
    if (g_page == 0) {
      render_receiver_status();
    } else {
      render_receiver_sender(g_page - 1);
    }
  }

  g_prev_ctrl_high = ctrl_high;
}
