#include <webserver.h>

WebPage::WebPage(const uint8_t port, fs::FS &filesystem) : m_port(port), m_webserver(AsyncWebServer(port)), m_websocket(AsyncWebSocket("/ws")), m_filesystem(filesystem)
{
    m_websocket.onEvent([this](AsyncWebSocket *server, AsyncWebSocketClient *client,
                               AwsEventType type, void *arg, uint8_t *data, size_t len)
                        { onWsEvent(server, client, type, arg, data, len); });

    m_webserver.addHandler(&m_websocket);
    m_webserver.serveStatic("/", m_filesystem, "/").setDefaultFile("index.html");

    m_webserver.on("/upload", HTTP_POST, 
        [this](AsyncWebServerRequest *request)
        { onUploadRequest(request); },
        [this](AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
        { onUploadHandler(request, filename, index, data, len, final); }
    );

    m_webserver.begin();
}

WebPage::~WebPage()
{
    m_webserver.removeHandler(&m_websocket);
    m_webserver.end();
}

void WebPage::sendWsData(const String &data){
    if (m_websocket.count()){
        m_websocket.textAll(data);
    }
}

void WebPage::onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, AwsEventType type, void *arg, uint8_t *data, size_t len)
{
    switch (type)
    {
    case WS_EVT_CONNECT:
        Serial.printf("WS client IP[%s]-ID[%u] CONNECTED\r\n", client->remoteIP().toString().c_str(), client->id());
        break;
    case WS_EVT_DISCONNECT:
        Serial.printf("WS client ID[%u] DISCONNECTED\r\n", client->remoteIP().toString().c_str(), client->id());
        break;
    }
}

void WebPage::onUploadRequest(AsyncWebServerRequest *request)
{
    if (m_upload_failed)
        request->send(500, "text/plain", "Upload failed");
    else
        request->send(200, "text/plain", "Upload successful");
}

void WebPage::onUploadHandler(AsyncWebServerRequest *request, const String &filename, size_t index, uint8_t *data, size_t len, bool final)
{
    if (index == 0) // only on first iteration to open file
    {
        m_upload_failed = false;
        String safeName = filename;
        int slashIndex = safeName.lastIndexOf('/');
        if (slashIndex >= 0)
            safeName = safeName.substring(slashIndex + 1);
        if (safeName.length() == 0)
        {
            m_upload_failed = true;
            LOG_ERROR("Invalid file name");
            return;
        }

        const std::filesystem::path filePath = std::filesystem::path(m_filesystem.mountpoint()) / safeName.c_str();
        if (m_filesystem.exists(filePath.c_str()))
            m_filesystem.remove(filePath.c_str());

        m_upload_file = m_filesystem.open(filePath.c_str(), FILE_WRITE);
        if (!m_upload_file)
        {
            m_upload_failed = true;
            LOG_ERROR("Failed to open upload file:", filePath.c_str());
            return;
        }
    }

    // Actual write of file data
    if (!m_upload_failed && m_upload_file)
    {
        if (m_upload_file.write(data, len) != len)
            m_upload_failed = true;
    }

    // close the file and save on final call
    if (final && m_upload_file)
    {
        m_upload_file.close();
        if (!m_upload_failed)
            LOG_INFO("Uploaded file to LittleFS:", filename.c_str());
    }
}
