/**
 *   ESP32 SvelteKit
 *
 *   A simple, secure and extensible framework for IoT projects for ESP32 platforms
 *   with responsive Sveltekit front-end built with TailwindCSS and DaisyUI.
 *   https://github.com/theelims/ESP32-sveltekit
 *
 *   Copyright (C) 2018 - 2023 rjwats
 *   Copyright (C) 2024 theelims
 *   Copyright (C) 2024 runeharlyk
 *
 *   All Rights Reserved. This software may be modified and distributed under
 *   the terms of the LGPL v3 license. See the LICENSE file for details.
 **/

#include <ESP32SvelteKit.h>

ESP32SvelteKit::ESP32SvelteKit(PsychicHttpServer *server, unsigned int numberEndpoints)
    : _server(server),
      _numberEndpoints(numberEndpoints),
      _taskManager(),
      _featureService(server),
      _securitySettingsService(server, &ESPFS),
      _wifiSettingsService(server, &ESPFS, &_securitySettingsService, &_socket),
      _wifiScanner(server, &_securitySettingsService),
      _wifiStatus(server, &_securitySettingsService),
      _apSettingsService(server, &ESPFS, &_securitySettingsService),
      _apStatus(server, &_securitySettingsService, &_apSettingsService),
      _socket(server, &_securitySettingsService, AuthenticationPredicates::IS_AUTHENTICATED),
#if FT_ENABLED(FT_NTP)
      _ntpSettingsService(server, &ESPFS, &_securitySettingsService),
      _ntpStatus(server, &_securitySettingsService),
#endif
#if FT_ENABLED(FT_UPLOAD_FIRMWARE)
      _uploadFirmwareService(server, &_securitySettingsService),
#endif
#if FT_ENABLED(FT_DOWNLOAD_FIRMWARE)
      _downloadFirmwareService(server, &_securitySettingsService, &_socket, &_taskManager),
#endif
#if FT_ENABLED(FT_SECURITY)
      _authenticationService(server, &_securitySettingsService),
#endif
#if FT_ENABLED(FT_SLEEP)
      _sleepService(server, &_securitySettingsService),
#endif
#if FT_ENABLED(FT_BATTERY)
      _batteryService(&_peripherals, &_socket),
#endif
#if FT_ENABLED(FT_ANALYTICS)
      _analyticsService(&_socket, &_taskManager),
#endif
#if FT_ENABLED(FT_CAMERA)
      _cameraService(server, &_taskManager, &_securitySettingsService),
      _cameraSettingsService(server, &ESPFS, &_securitySettingsService, &_socket),
#endif
      _restartService(server, &_securitySettingsService),
      _factoryResetService(server, &ESPFS, &_securitySettingsService),
      _systemStatus(server, &_securitySettingsService),
      _fileExplorer(server, &_securitySettingsService),
#if FT_ENABLED(FT_MOTION)
      _motionService(_server, &_socket, &_securitySettingsService, &_taskManager),
#endif
#if FT_ENABLED(FT_WS2812)
      _ledService(&_taskManager),
#endif
#if FT_ENABLED(FT_SERVO)
      _servoController(server, &ESPFS, &_securitySettingsService, &_socket),
#endif
      _peripherals(server, &ESPFS, &_securitySettingsService, &_socket) {
}

void ESP32SvelteKit::begin() {
    ESP_LOGV("ESP32SvelteKit", "Loading settings from files system");
    ESP_LOGI("Running Firmware Version: %s", APP_VERSION);
    ESPFS.begin(true);

    _wifiSettingsService.initWiFi();

    setupServer();

    setupMDNS();

    startServices();

    ESP_LOGV("ESP32SvelteKit", "Starting loop task");
}

void ESP32SvelteKit::setupServer() {
    _server->config.max_uri_handlers = _numberEndpoints;
    _server->listen(80);

#ifdef EMBED_WWW
    ESP_LOGV("ESP32SvelteKit", "Registering routes from PROGMEM static resources");
    WWWData::registerRoutes([&](const String &uri, const String &contentType, const uint8_t *content, size_t len) {
        PsychicHttpRequestCallback requestHandler = [contentType, content, len](PsychicRequest *request) {
            PsychicResponse response(request);
            response.setCode(200);
            response.setContentType(contentType.c_str());
            response.addHeader("Content-Encoding", "gzip");
            response.addHeader("Cache-Control", "public, immutable, max-age=31536000");
            response.setContent(content, len);
            return response.send();
        };
        PsychicWebHandler *handler = new PsychicWebHandler();
        handler->onRequest(requestHandler);
        _server->on(uri.c_str(), HTTP_GET, handler);

        // Set default end-point for all non matching requests
        // this is easier than using webServer.onNotFound()
        if (uri.equals("/index.html")) {
            _server->defaultEndpoint->setHandler(handler);
        }
    });
#else
    // Serve static resources from /www/
    ESP_LOGV("ESP32SvelteKit", "Registering routes from FS /www/ static resources");
    _server->serveStatic("/_app/", ESPFS, "/www/_app/");
    _server->serveStatic("/favicon.png", ESPFS, "/www/favicon.png");
    //  Serving all other get requests with "/www/index.htm"
    _server->onNotFound([](PsychicRequest *request) {
        if (request->method() == HTTP_GET) {
            PsychicFileResponse response(request, ESPFS, "/www/index.html", "text/html");
            return response.send();
            // String url = "http://" + request->host() + "/index.html";
            // request->redirect(url.c_str());
        }
    });
#endif
#ifdef SERVE_CONFIG_FILES
    _server->serveStatic("/api/config/", ESPFS, "/config/");
#endif

#if defined(ENABLE_CORS)
    ESP_LOGV("ESP32SvelteKit", "Enabling CORS headers");
    DefaultHeaders::Instance().addHeader("Access-Control-Allow-Origin", CORS_ORIGIN);
    DefaultHeaders::Instance().addHeader("Access-Control-Allow-Headers", "Accept, Content-Type, Authorization");
    DefaultHeaders::Instance().addHeader("Access-Control-Allow-Credentials", "true");
#endif
    DefaultHeaders::Instance().addHeader("Server", _appName);
}

void ESP32SvelteKit::setupMDNS() {
    ESP_LOGV("ESP32SvelteKit", "Starting MDNS");
    MDNS.begin(_wifiSettingsService.getHostname().c_str());
    MDNS.setInstanceName(_appName);
    MDNS.addService("http", "tcp", 80);
    MDNS.addService("ws", "tcp", 80);
    MDNS.addServiceTxt("http", "tcp", "Firmware Version", APP_VERSION);
}

void ESP32SvelteKit::startServices() {
    _apStatus.begin();
    _socket.begin();
    _apSettingsService.begin();
    _factoryResetService.begin();
    _featureService.begin();
    _restartService.begin();
    _systemStatus.begin();
    _wifiSettingsService.begin();
    _wifiScanner.begin();
    _wifiStatus.begin();

#if FT_ENABLED(FT_UPLOAD_FIRMWARE)
    _uploadFirmwareService.begin();
#endif
#if FT_ENABLED(FT_DOWNLOAD_FIRMWARE)
    _downloadFirmwareService.begin();
#endif
#if FT_ENABLED(FT_NTP)
    _ntpSettingsService.begin();
    _ntpStatus.begin();
#endif
#if FT_ENABLED(FT_SECURITY)
    _authenticationService.begin();
    _securitySettingsService.begin();
#endif
#if FT_ENABLED(FT_ANALYTICS)
    _analyticsService.begin();
#endif
#if FT_ENABLED(FT_SLEEP)
    _sleepService.begin();
#endif
#if FT_ENABLED(FT_BATTERY)
    _batteryService.begin();
#endif
    _taskManager.begin();
    _fileExplorer.begin();
#if FT_ENABLED(FT_MOTION)
    _motionService.begin();
#endif
#if FT_ENABLED(FT_CAMERA)
    _cameraService.begin();
    _cameraSettingsService.begin();
#endif
    _peripherals.begin();
#if FT_ENABLED(FT_SERVO)
    _servoController.begin();
    _servoController.configure();
#endif
#if FT_ENABLED(FT_WS2812)
    _ledService.begin();
#endif
}

void IRAM_ATTR ESP32SvelteKit::loop() {
    while (1) {
        _wifiSettingsService.loop();
        _apSettingsService.loop();
#if FT_ENABLED(FT_ANALYTICS)
        _analyticsService.loop();
#endif
#if FT_ENABLED(FT_BATTERY)
        _batteryService.loop();
#endif
#if FT_ENABLED(FT_MOTION)
        _motionService.loop();
#endif
#if FT_ENABLED(FT_SERVO)
        _servoController.loop();
#endif
#if FT_ENABLED(FT_WS2812)
        _ledService.loop();
#endif
        _peripherals.loop();
        delay(20);
    }
}
