#include <Arduino.h>

const char* DEVICE_TYPE = "hatch";
const char* FW_VERSION  = "1.0 dev";
const uint8_t CHANNEL_NUM = 2;  // 0: открыть, 1: закрыть

#include <sh_core.h>
#include "HatchLogic.h"

// -------------------- EEPROM --------------------
const uint16_t HATCH_EEPROM_BASE = getDeviceEepromStart();
uint16_t HATCH_EEPROM_ADDR = HATCH_EEPROM_BASE;  // float, 4 байта

// -------------------- Силовое реле --------------------
// GPIO 26 — безопасный пин на ESP32 WROOM/DevKit.
// Поменяй если используешь другой модуль.
uint8_t HATCH_POWER_RELAY_PIN = 18;

// -------------------- Состояние --------------------
float hatchPosition = 0.0f;

// -------------------- JSON helpers --------------------

static String extractJsonStringValue(const String &body, const String &key) {
  String pattern = "\"" + key + "\"";
  int keyIndex = body.indexOf(pattern);
  if (keyIndex < 0) return "";
  int colonIndex = body.indexOf(':', keyIndex);
  if (colonIndex < 0) return "";
  int firstQuote = body.indexOf('"', colonIndex + 1);
  if (firstQuote < 0) return "";
  int secondQuote = body.indexOf('"', firstQuote + 1);
  if (secondQuote < 0) return "";
  return body.substring(firstQuote + 1, secondQuote);
}

static float extractJsonFloatValue(const String &body, const String &key) {
  String pattern = "\"" + key + "\"";
  int keyIndex = body.indexOf(pattern);
  if (keyIndex < 0) return -1.0f;
  int colonIndex = body.indexOf(':', keyIndex);
  if (colonIndex < 0) return -1.0f;

  int i = colonIndex + 1;
  while (i < (int)body.length() &&
         (body[i] == ' ' || body[i] == '\t' || body[i] == '\n' || body[i] == '\r')) i++;

  bool quoted = (i < (int)body.length() && body[i] == '"');
  if (quoted) i++;

  String numStr = "";
  while (i < (int)body.length() &&
         (body[i] == '-' || body[i] == '.' || (body[i] >= '0' && body[i] <= '9'))) {
    numStr += body[i++];
  }
  if (numStr.length() == 0) return -1.0f;
  return numStr.toFloat();
}

// -------------------- appendStatusJsonFields --------------------

void appendStatusJsonFields(String &json) {
  uint8_t pct = (uint8_t)((hatchPosition / HATCH_LIMIT_MAX) * 100.0f + 0.5f);
  if (pct > 100) pct = 100;

  const char* stateStr = "closed";
  if (hatchGetState() == HATCH_STATE_OPENING)    stateStr = "opening";
  else if (hatchGetState() == HATCH_STATE_CLOSING)    stateStr = "closing";
  else if (hatchGetState() == HATCH_STATE_CALIBRATING) stateStr = "calibrating";
  else if (hatchPosition > 0.0f)                 stateStr = "open";

  json += ",\"hatch\":{";
  json += "\"state\":\"";
  json += stateStr;
  json += "\",";
  json += "\"position_sec\":";
  json += String(hatchPosition, 2);
  json += ",\"position_pct\":";
  json += String(pct);
  json += "}";
}

// -------------------- appendAboutJsonFields --------------------

void appendAboutJsonFields(String &json) {
  json += ",\"channels\":" + String(CHANNEL_NUM);
}

// -------------------- deviceHandleAction --------------------

bool deviceHandleAction(const String &action,
                        const String &paramsJson,
                        String &errorCode,
                        String &errorMessage)
{
  if (action == "open") {
    float timeSec = extractJsonFloatValue(paramsJson, "time");
    float pct     = extractJsonFloatValue(paramsJson, "percent");

    if (timeSec <= 0.0f && pct <= 0.0f) {
      errorCode    = "IllegalActionOrParams";
      errorMessage = "Parameter 'time' or 'percent' must be > 0";
      return false;
    }

    if (pct > 0.0f) {
      if (pct > 100.0f) pct = 100.0f;
      timeSec = (pct / 100.0f) * HATCH_LIMIT_MAX;
    }

    HatchActionResult res = hatchOpen(timeSec);

    switch (res) {
      case HATCH_OK:
        return true;

      case HATCH_ERR_ALREADY_AT_LIMIT:
        errorCode    = "IllegalActionOrParams";
        errorMessage = "Hatch is already fully open";
        return false;

      case HATCH_ERR_BUSY:
        errorCode    = "DeviceBusy";
        errorMessage = "Hatch is already moving";
        return false;

      case HATCH_ERR_CALIBRATION_FAILED:
        errorCode    = "CalibrationFailed";
        errorMessage = "Limit switch did not trigger during calibration";
        return false;

      default:
        errorCode    = "IllegalActionOrParams";
        errorMessage = "Open failed";
        return false;
    }
  }

  if (action == "close") {
    float timeSec = extractJsonFloatValue(paramsJson, "time");
    float pct     = extractJsonFloatValue(paramsJson, "percent");

    if (timeSec <= 0.0f && pct <= 0.0f) {
      errorCode    = "IllegalActionOrParams";
      errorMessage = "Parameter 'time' or 'percent' must be > 0";
      return false;
    }

    if (pct > 0.0f) {
      if (pct > 100.0f) pct = 100.0f;
      timeSec = (pct / 100.0f) * HATCH_LIMIT_MAX;
    }

    HatchActionResult res = hatchClose(timeSec);

    switch (res) {
      case HATCH_OK:
        return true;

      case HATCH_ERR_ALREADY_CLOSED:
        errorCode    = "IllegalActionOrParams";
        errorMessage = "Hatch is already closed";
        return false;

      case HATCH_ERR_BUSY:
        errorCode    = "DeviceBusy";
        errorMessage = "Hatch is already moving";
        return false;

      default:
        errorCode    = "IllegalActionOrParams";
        errorMessage = "Close failed";
        return false;
    }
  }

  errorCode    = "IllegalActionOrParams";
  errorMessage = "Unknown action. Supported: open, close";
  return false;
}

// -------------------- deviceHandleReset --------------------

void deviceHandleReset() {
  hatchPosition = 0.0f;
  hatchSavePosition();

  // Сбрасываем EEPROM устройства
  EEPROM.begin(EEPROM_SIZE);
  for (uint16_t addr = DEVICE_EEPROM_START; addr < EEPROM_SIZE; addr++) {
    EEPROM.write(addr, 0xFF);
  }
  EEPROM.commit();
  EEPROM.end();
}

// -------------------- setup / loop --------------------

void setup() {
  // Принудительно выключаем все реле ДО coreSetup —
  // чтобы избежать случайного срабатывания при boot.

  // Силовое реле — хардкод, выключаем первым
  if (HATCH_POWER_RELAY_PIN != SH_PIN_UNUSED) {
    pinMode(HATCH_POWER_RELAY_PIN, OUTPUT);
    digitalWrite(HATCH_POWER_RELAY_PIN, HATCH_POWER_RELAY_IDLE);
  }

  // Загружаем карту каналов из EEPROM чтобы знать пины сигнальных реле
  // (coreSetup ещё не вызван, но EEPROM читать можно)
  if (is_channels_schema_valid()) {
    load_channels_schema();
  } else {
    init_default_channels_schema();
  }

  // Сигнальные реле — выключаем по карте каналов
  // HIGH = реле выключено для активного LOW модуля
  const uint8_t earlyPins[] = { sh_channel_pin(HATCH_CH_OPEN), sh_channel_pin(HATCH_CH_CLOSE) };
  for (uint8_t i = 0; i < 2; i++) {
    if (earlyPins[i] != SH_PIN_UNUSED) {
      pinMode(earlyPins[i], OUTPUT);
      digitalWrite(earlyPins[i], HIGH); // HIGH = реле выключено (активный LOW)
    }
  }

  coreSetup();

  // Инициализация силового реле (безопасное состояние — выключено)
  if (HATCH_POWER_RELAY_PIN != SH_PIN_UNUSED) {
    pinMode(HATCH_POWER_RELAY_PIN, OUTPUT);
    digitalWrite(HATCH_POWER_RELAY_PIN, HATCH_POWER_RELAY_IDLE);
  }

  // Инициализация пинов управления (сначала уровень, потом режим — избегаем глитча)
  for (uint8_t ch = 0; ch < CHANNEL_NUM; ch++) {
    uint8_t pin = sh_channel_pin(ch);
    if (pin == SH_PIN_UNUSED) continue;
    bool idleLevel = sh_channel_is_inverted(ch) ? HIGH : LOW;
    digitalWrite(pin, idleLevel);
    pinMode(pin, OUTPUT);
  }

  // Инициализация пина концевика
  uint8_t fbPin = sh_channel_feedback_pin(HATCH_CH_CLOSE);
  if (fbPin != SH_PIN_UNUSED) {
    pinMode(fbPin, INPUT_PULLDOWN);
  }

  // Загрузка сохранённой позиции
  hatchLoadPosition();

  Serial.print(F("Hatch position loaded: "));
  Serial.print(hatchPosition, 2);
  Serial.println(F(" sec"));
}

void loop() {
  coreLoop();
  hatchTick();
}
