#include "webserver.h"
#include "esp_event.h"
#include "esp_http_server.h"
#include "esp_log.h"
#include "esp_mac.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "nvs_flash.h"
#include <stdarg.h>
#include <stdio.h>
#include <string.h>

#define AP_WIFI_SSID "AutoSpa"
#define AP_WIFI_PASS "autospa123"
#define AP_MAX_CONN 4
#define AP_WIFI_CHANNEL 1

#define STA_WIFI_SSID "Big_Blue_House"
#define STA_WIFI_PASS "tDMiar*2024"

static const char *TAG = "webserver";
static volatile int s_check_id_req = 0;

// Log buffer
#define LOG_BUF_SIZE 4096
static char log_buffer[LOG_BUF_SIZE];
static int log_head = 0;

void web_log(const char *fmt, ...) {
  va_list args;
  va_start(args, fmt);
  char temp[256];
  int len = vsnprintf(temp, sizeof(temp), fmt, args);
  va_end(args);

  if (len > 0) {
    if (log_head + len < LOG_BUF_SIZE - 1) {
      memcpy(log_buffer + log_head, temp, len);
      log_head += len;
      log_buffer[log_head] = '\0';
    } else {
      log_head = 0;
      memcpy(log_buffer + log_head, temp, len);
      log_head += len;
      log_buffer[log_head] = '\0';
    }
    ESP_LOGI(TAG, "%s", temp);
  }
}

int check_id_requested(void) {
  if (s_check_id_req) {
    s_check_id_req = 0;
    return 1;
  }
  return 0;
}

/* WiFi AP Event Handler */
/* WiFi Event Handler */
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
                               int32_t event_id, void *event_data) {
  if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_AP_STACONNECTED) {
    wifi_event_ap_staconnected_t *event =
        (wifi_event_ap_staconnected_t *)event_data;
    ESP_LOGI(TAG, "station " MACSTR " joined, AID=%d", MAC2STR(event->mac),
             event->aid);
    web_log("Client connected!\n");
  } else if (event_base == WIFI_EVENT &&
             event_id == WIFI_EVENT_AP_STADISCONNECTED) {
    wifi_event_ap_stadisconnected_t *event =
        (wifi_event_ap_stadisconnected_t *)event_data;
    ESP_LOGI(TAG, "station " MACSTR " left, AID=%d", MAC2STR(event->mac),
             event->aid);
    web_log("Client disconnected.\n");
  } else if (event_base == WIFI_EVENT && event_id == WIFI_EVENT_STA_START) {
    esp_wifi_connect();
  } else if (event_base == WIFI_EVENT &&
             event_id == WIFI_EVENT_STA_DISCONNECTED) {
    esp_wifi_connect();
    ESP_LOGI(TAG, "retry to connect to the AP");
    web_log("Retry to connect to the AP\n");
  } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) {
    ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
    ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
    web_log("Got IP: " IPSTR "\n", IP2STR(&event->ip_info.ip));
    start_webserver();
  }
}

void wifi_init_ap(void) {
  ESP_ERROR_CHECK(esp_netif_init());
  ESP_ERROR_CHECK(esp_event_loop_create_default());
  esp_netif_create_default_wifi_ap();

  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));

  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));

  wifi_config_t wifi_config = {
      .ap =
          {
              .ssid = AP_WIFI_SSID,
              .ssid_len = strlen(AP_WIFI_SSID),
              .channel = AP_WIFI_CHANNEL,
              .password = AP_WIFI_PASS,
              .max_connection = AP_MAX_CONN,
              .authmode = WIFI_AUTH_WPA2_PSK,
              .pmf_cfg = {.required = true},
          },
  };

  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP));
  ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
  ESP_ERROR_CHECK(esp_wifi_start());

  ESP_LOGI(TAG, "SoftAP started. SSID: %s, Password: %s", AP_WIFI_SSID,
           AP_WIFI_PASS);
  ESP_LOGI(TAG, "Connect to this WiFi and browse to http://192.168.4.1:9099");
}

void wifi_init_sta(void) {
  ESP_ERROR_CHECK(esp_netif_init());
  ESP_ERROR_CHECK(esp_event_loop_create_default());
  esp_netif_create_default_wifi_sta();

  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));

  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      WIFI_EVENT, ESP_EVENT_ANY_ID, &wifi_event_handler, NULL, NULL));
  ESP_ERROR_CHECK(esp_event_handler_instance_register(
      IP_EVENT, IP_EVENT_STA_GOT_IP, &wifi_event_handler, NULL, NULL));

  wifi_config_t wifi_config = {
      .sta =
          {
              .ssid = STA_WIFI_SSID,
              .password = STA_WIFI_PASS,
              /* Setting a password implies station will connect to all security
               * modes including WEP/WPA. However these modes are deprecated and
               * not advised to be used. Incase your Access point doesn't
               * support WPA2, these mode can be enabled by commenting below
               * line */
              .threshold.authmode = WIFI_AUTH_WPA2_PSK,
          },
  };
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
  ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
  ESP_ERROR_CHECK(esp_wifi_start());

  ESP_LOGI(TAG, "wifi_init_sta finished.");
}

/* HTTP Server Handlers */
static const char *index_html =
    "<!DOCTYPE html><html><head><title>AD5940 Control</title></head><body>"
    "<h1>AD5940 ESP32-S3 Dashboard</h1>"
    "<button onclick=\"sendCommand('check_id')\">Check ID</button>"
    "<h2>Console Log</h2>"
    "<pre id=\"console\" "
    "style=\"background:#eee;padding:10px;height:300px;overflow:auto;\"></pre>"
    "<script>"
    "function sendCommand(cmd) {"
    "  fetch('/api/command', {method:'POST', "
    "body:JSON.stringify({command:cmd})});"
    "}"
    "setInterval(() => {"
    "  fetch('/api/log').then(r => r.text()).then(t => {"
    "    if(t) document.getElementById('console').textContent = t;"
    "  });"
    "}, 1000);"
    "</script></body></html>";

static esp_err_t root_get_handler(httpd_req_t *req) {
  ESP_LOGI(TAG, "Request received for %s", req->uri);
  httpd_resp_send(req, index_html, HTTPD_RESP_USE_STRLEN);
  return ESP_OK;
}

static esp_err_t command_post_handler(httpd_req_t *req) {
  char buf[100];
  int ret, remaining = req->content_len;

  if (remaining >= sizeof(buf)) {
    remaining = sizeof(buf) - 1;
  }

  ret = httpd_req_recv(req, buf, remaining);
  if (ret <= 0)
    return ESP_FAIL;
  buf[ret] = '\0';

  if (strstr(buf, "check_id")) {
    s_check_id_req = 1;
    web_log("Command received: Check ID\n");
  }

  httpd_resp_send(req, "OK", HTTPD_RESP_USE_STRLEN);
  return ESP_OK;
}

static esp_err_t log_get_handler(httpd_req_t *req) {
  httpd_resp_set_type(req, "text/plain");
  httpd_resp_send(req, log_buffer, HTTPD_RESP_USE_STRLEN);
  // Note: This sends the whole buffer every time.
  // Optimization: implement cursor or clear on read.
  // For now, this is enough for "Check ID" simple verification.
  return ESP_OK;
}

void start_webserver(void) {
  httpd_handle_t server = NULL;
  httpd_config_t config = HTTPD_DEFAULT_CONFIG();
  config.server_port = 9099;
  config.lru_purge_enable = true;

  ESP_LOGI(TAG, "Starting web server"); // Log to serial for debug too
  if (httpd_start(&server, &config) == ESP_OK) {
    ESP_LOGI(TAG, "Web server started on port %d", config.server_port);
    httpd_uri_t root = {.uri = "/",
                        .method = HTTP_GET,
                        .handler = root_get_handler,
                        .user_ctx = NULL};
    httpd_register_uri_handler(server, &root);

    httpd_uri_t cmd = {.uri = "/api/command",
                       .method = HTTP_POST,
                       .handler = command_post_handler,
                       .user_ctx = NULL};
    httpd_register_uri_handler(server, &cmd);

    httpd_uri_t log_uri = {.uri = "/api/log",
                           .method = HTTP_GET,
                           .handler = log_get_handler,
                           .user_ctx = NULL};
    httpd_register_uri_handler(server, &log_uri);
  } else {
    ESP_LOGE(TAG, "Error starting server!");
  }
}
