#pragma once

extern uint16_t relayDurations[16];

void loadRelayTimings();
void saveRelayTimings();

// - - - - - - - - - - - - - - - - - - - - - - - - - - -

// Sets Incoming Relay Durations to RAM and then call funtion to save them to file.
void updateRelayTimings(JsonVariant doc) {
    // Iterate through the relays in the JSON payload
    for (uint8_t i = 0; i < 16; i++) {
        String key = String("b") + (i + 1); // Generate "b1", "b2", ...
        if (doc.containsKey(key)) {
            relayDurations[i] = doc[key].as<uint16_t>();
            LOG_DEBUG("Relay %d duration s1et to %d ms\n", i + 1, relayDurations[i]);
        } else {
            LOG_DEBUG("Relay %d not found in JSON payload. Keeping previous duration: %d ms\n", i + 1, relayDurations[i]);
        }
    }
		saveRelayTimings();
		LOG_INFO("Updated Relay Timings.")
}

// Save file "filename" with data: "data" to the "dirPath" directory of the SD card
void saveFileToSD(const char* dirPath, const char* filename, const char* data) {
    // Initialize SD (if not already done)
    if (!SD.begin(SD_CS)) {
        LOG_ERROR("SD Card not initialized!");
        return;
    }

    // Make sure directory exists
    if (!SD.exists(dirPath)) {
        SD.mkdir(dirPath);
    }

    // Build full path
    String fullPath = String(dirPath);
    if (!fullPath.endsWith("/")) fullPath += "/";
    fullPath += filename;

    File file = SD.open(fullPath.c_str(), FILE_WRITE);
    if (!file) {
        LOG_ERROR("Failed to open file: %s", fullPath.c_str());
        return;
    }

    file.print(data);
    file.close();
    LOG_INFO("File %s saved successfully.\n", fullPath.c_str());
}

// Saves Relay Durations from RAM, into a file
void saveRelayTimings() {
    StaticJsonDocument<512> doc; // Adjust size if needed

    // Populate the JSON object with relay durations
    for (uint8_t i = 0; i < 16; i++) {
        String key = String("b") + (i + 1);
        doc[key] = relayDurations[i];
    }

		char buffer[512];
		size_t len = serializeJson(doc, buffer, sizeof(buffer));
		if (len == 0) {
			LOG_ERROR("Failed to serialize JSON.");
			return;
		}

    const char * path = "/settings";
		const char * filename = "relayTimings.json";
		saveFileToSD(path, filename, buffer);
}

// Loads Relay Durations from file into RAM (called during boot)
void loadRelayTimings() {

    if (!SD.begin(SD_CS)) {
        LOG_ERROR("SD Card not initialized. Using default relay timings.");
        return;
    }

    File file = SD.open("/settings/relayTimings.json", FILE_READ);
    if (!file) {
      LOG_ERROR("Settings file not found on SD. Using default relay timings.");
    return;
    }
    // Parse the JSON file
    StaticJsonDocument<512> doc; // Adjust size if needed
    DeserializationError error = deserializeJson(doc, file);
    file.close();
    if (error) {
        LOG_ERROR("Failed to parse settings from SD. Using default relay timings.");
        return;
    }

    // Populate relayDurations array
    for (uint8_t i = 0; i < 16; i++) {
        String key = String("b") + (i + 1);
        if (doc.containsKey(key)) {
            relayDurations[i] = doc[key].as<uint16_t>();
            LOG_DEBUG("Loaded relay %d duration: %d ms\n", i + 1, relayDurations[i]);
        }
    }

}

// Function to sync time with NTP server and update RTC
void syncTimeWithNTP() {
  // Connect to Wi-Fi if not already connected
  if (WiFi.status() != WL_CONNECTED) {
    LOG_DEBUG("Connecting to Wi-Fi...");
    WiFi.begin(ssid, password);
    while (WiFi.status() != WL_CONNECTED) {
      delay(500);
      LOG_DEBUG(".");
    }
    LOG_DEBUG("\nWi-Fi connected!");
  }

  // Configure NTP
  configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);

  // Sync time from NTP server
  LOG_DEBUG("Syncing time with NTP server...");
  struct tm timeInfo;
  if (!getLocalTime(&timeInfo)) {
    LOG_DEBUG("Failed to obtain time from NTP server!");
    return;
  }

  // Update RTC with synchronized time
  rtc.adjust(DateTime(timeInfo.tm_year + 1900, timeInfo.tm_mon + 1, timeInfo.tm_mday,
                      timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec));

  // Log synchronized time
  LOG_INFO("Time synced with NTP server.");
  LOG_DEBUG("Synced time: %02d:%02d:%02d, %02d/%02d/%04d",
            timeInfo.tm_hour, timeInfo.tm_min, timeInfo.tm_sec,
            timeInfo.tm_mday, timeInfo.tm_mon + 1, timeInfo.tm_year + 1900);
}

// Call this function with the Firebase URL and desired local filename
bool downloadFileToSD(const String& url, const String& directory, const String& filename) {
  LOG_INFO("HTTP Starting download...");

  HTTPClient http;
  http.begin(url);
  int httpCode = http.GET();

  if (httpCode != HTTP_CODE_OK) {
    LOG_ERROR("(HTTP) GET failed, error: %s\n", http.errorToString(httpCode).c_str());
    http.end();
    return false;
  }

  if (!SD.begin(SD_CS)) {
    LOG_ERROR("SD Card init failed!");
    http.end();
    return false;
  }

  // Ensure the directory ends with '/'
  String dirPath = directory;
  if (!dirPath.endsWith("/")) dirPath += "/";

  // Create directory if it doesn't exist
  SD.mkdir(dirPath.c_str());

  String fullPath = dirPath + filename;

  File file = SD.open(fullPath.c_str(), FILE_WRITE);
  if (!file) {
    LOG_ERROR("SD Failed to open file for writing: %s", fullPath.c_str());
    http.end();
    return false;
  }

  WiFiClient* stream = http.getStreamPtr();
  uint8_t buffer[1024];
  int bytesRead;

  while (http.connected() && (bytesRead = stream->readBytes(buffer, sizeof(buffer))) > 0) {
    file.write(buffer, bytesRead);
  }

  file.close();
  http.end();
  LOG_INFO("HTTP Download complete, file saved to: %s", fullPath.c_str());
  return true;
}


// Returns the list of melodies (the filenames) currently inside the SD Card.
String listFilesAsJson(const char* dirPath) {
  if (!SD.begin(SD_CS)) {
    LOG_ERROR("SD init failed");
    return "{}";
  }

  File dir = SD.open(dirPath);
  if (!dir || !dir.isDirectory()) {
    LOG_ERROR("Directory not found");
    return "{}";
  }

  DynamicJsonDocument doc(1024);  // Adjust size if needed
  JsonArray fileList = doc.createNestedArray("files");

  File file = dir.openNextFile();
  while (file) {
    if (!file.isDirectory()) {
      fileList.add(file.name());
    }
    file = dir.openNextFile();
  }

  String json;
  serializeJson(doc, json);
  return json;
}

// Prints the Steps of a Melody from a file using its filename
void printMelodyFile(const String& filename) {
  if (!SD.begin(SD_CS)) {
    LOG_ERROR("SD Card not initialized.");
    return;
  }

  File file = SD.open("/melodies/" + filename, FILE_READ);
  if (!file) {
    Serial.println("Failed to open Melody file for reading");
    return;
  }

  Serial.printf("---- Contents of %s ----\n", filename.c_str());

  uint16_t step;
  int index = 0;

  while (file.available() >= 2) {
    uint8_t low = file.read();
    uint8_t high = file.read();
    step = (high << 8) | low;
    Serial.printf("Step %5d: 0x%04X (%d)\n", index++, step, step);
  }

  file.close();
  Serial.println("---- End of File ----");
}

// Prints the contents of a Text file using its filename
void printFileAsText(const String& path, const String& filename) {
  if (!SD.begin(SD_CS)) {
    LOG_ERROR("SD Card not initialized.");
    return;
  }

  String fullPath = path;
  if (!fullPath.endsWith("/")) fullPath += "/";
  fullPath += filename;

  File file = SD.open(fullPath, FILE_READ);
  if (!file) {
    Serial.println("Failed to open file for reading");
    return;
  }

  Serial.printf("---- Contents of %s ----\n", filename.c_str());

  while (file.available()) {
    String line = file.readStringUntil('\n');
    Serial.println(line);
  }

  file.close();
  Serial.println("---- End of File ----");
}

// Downloads a new melody from HTTP
void addMelody(JsonVariant doc) {

    LOG_INFO("Trying Saving...");
    const char* url = doc["url"];
    const char* filename = doc["filename"];
    downloadFileToSD(url, "/melodies", filename);


}

// Checks the onboard SD Card for new firmware
void checkFirmwareUpdate() {
  if (!SD.begin(SD_CS)) {
    Serial.println("SD init failed");
    return;
  }

  File updateBin = SD.open("/firmware/update.bin");
  if (!updateBin) {
    Serial.println("No update.bin found");
    return;
  }

  size_t updateSize = updateBin.size();
  if (updateSize == 0) {
    Serial.println("Empty update file");
    updateBin.close();
    return;
  }

  Serial.println("Starting firmware update...");

  if (Update.begin(updateSize)) {
    size_t written = Update.writeStream(updateBin);
    if (written == updateSize) {
      Serial.println("Update written successfully");
    } else {
      Serial.printf("Written only %d/%d bytes\n", written, updateSize);
    }

    if (Update.end()) {
      Serial.println("Update finished!");
      if (Update.isFinished()) {
        Serial.println("Update complete. Rebooting...");
        updateBin.close();
        SD.remove("/firmware/update.bin");  // optional cleanup
        ESP.restart();
      } else {
        Serial.println("Update not complete");
      }
    } else {
      Serial.printf("Update error: %s\n", Update.errorString());
    }
  } else {
    Serial.println("Not enough space to begin update");
  }

  updateBin.close();
}



// UNSUSED FUNCTIONS.

// void startConfigPortal() {
//   WiFi.mode(WIFI_AP);
//   WiFi.softAP("Device_Config", "12345678");
//   Serial.println("AP mode started. Connect to 'Device_Config'.");

//   // Serve the configuration page
//   server.on("/", HTTP_GET, []() {
//     server.send(200, "text/html", generateConfigPageHTML());
//   });

//   // Handle form submission
//   server.on("/save", HTTP_POST, []() {
//     ssid = server.arg("ssid");
//     password = server.arg("password");
//     mqttHost.fromString(server.arg("mqttHost"));
//     mqttUser = server.arg("mqttUser");
//     mqttPassword = server.arg("mqttPassword");

//     saveSettings();  // Save new settings to SPIFFS
//     server.send(200, "text/plain", "Settings saved! Rebooting...");
//     delay(1000);
//     ESP.restart();
//   });

//   server.begin();
// }

// // Save settings to SPIFFS
// void saveSettings() {
//   StaticJsonDocument<512> doc;
//   doc["ssid"] = ssid;
//   doc["password"] = password;
//   doc["mqttHost"] = mqttHost.toString();
//   doc["mqttUser"] = mqttUser;
//   doc["mqttPassword"] = mqttPassword;

//   File configFile = SPIFFS.open(CONFIG_FILE, "w");
//   if (!configFile) {
//     Serial.println("Failed to open config file for writing.");
//     return;
//   }
//   serializeJson(doc, configFile);
//   configFile.close();
//   Serial.println("Settings saved to SPIFFS.");
// }

// // Load settings from SPIFFS
// void loadSettings() {
//   if (!SPIFFS.exists(CONFIG_FILE)) {
//     Serial.println("Config file not found. Using defaults.");
//     return;
//   }

//   File configFile = SPIFFS.open(CONFIG_FILE, "r");
//   if (!configFile) {
//     Serial.println("Failed to open config file.");
//     return;
//   }

//   StaticJsonDocument<512> doc;
//   DeserializationError error = deserializeJson(doc, configFile);
//   if (error) {
//     Serial.println("Failed to parse config file.");
//     return;
//   }

//   ssid = doc["ssid"].as<String>();
//   password = doc["password"].as<String>();
//   mqttHost.fromString(doc["mqttHost"].as<String>());
//   mqttUser = doc["mqttUser"].as<String>();
//   mqttPassword = doc["mqttPassword"].as<String>();

//   configFile.close();
//   Serial.println("Settings loaded from SPIFFS.");
// }

// // Generate HTML page for configuration
// String generateConfigPageHTML() {
//   String page = R"rawliteral(
//     <!DOCTYPE html>
//     <html>
//     <body>
//       <h2>Device Configuration</h2>
//       <form action="/save" method="POST">
//         WiFi SSID: <input type="text" name="ssid" value=")rawliteral" +
//                ssid + R"rawliteral("><br>
//         WiFi Password: <input type="password" name="password" value=")rawliteral" +
//                password + R"rawliteral("><br>
//         MQTT Host: <input type="text" name="mqttHost" value=")rawliteral" +
//                mqttHost.toString() + R"rawliteral("><br>
//         MQTT Username: <input type="text" name="mqttUser" value=")rawliteral" +
//                mqttUser + R"rawliteral("><br>
//         MQTT Password: <input type="password" name="mqttPassword" value=")rawliteral" +
//                mqttPassword + R"rawliteral("><br>
//         <input type="submit" value="Save">
//       </form>
//     </body>
//     </html>
//   )rawliteral";
//   return page;
// }
