#pragma once

#include <ArduinoJson.h>
#include <HTTPClient.h>
#include <Update.h>
#include <FS.h>
#include <memory>
#include <ESPAsyncWebServer.h>
#include "AppVersion.h"

//#define DEFAULT_MANIFEST_URL "https://storage.googleapis.com/boothifier/latest/"
#define DEFAULT_MANIFEST_URL "https://minio.boothwizard.com/boothifier/latest/"
#define BUFFER_SIZE 4096

extern TaskHandle_t Update_Task_Handle;

/**
 * @brief Version information structure
 */


//bool versionCompare(Version& remoteVersion, Version& localVersion);

extern Version otaVersion;

/**
 * @brief File information structure
 */
struct FileInfo {
    String remotePath;   ///< Path on remote server
    String localPath;    ///< Path in local filesystem
    String md5;         ///< MD5 hash for verification
    //size_t size;        ///< File size in bytes
};

/**
 * @class AppUpdater
 * @brief Handles firmware and filesystem updates
 */
class AppUpdater {
    public:
        Version localVersion;
        Version otaVersion;
    // Base URL (bucket) for update resources. Supports either Google Cloud Storage or MinIO (or any HTTP host)
    String baseUrl;
        const char* appName;
        const char* manifestName;
        JsonDocument jsonManifest;
        JsonArray jsonFilesArray;

        /**
         * @brief Update status enumeration
         */
        enum class UpdateStatus {
            IDLE,           ///< No update in progress
            MESSAGE,        ///< Update message
            DOWNLOADING,    ///< Downloading files
            VERIFYING,      ///< Verifying file integrity
            FILE_SKIPPED,       ///< File already up to date
            FILE_SAVED,         ///< Saving to filesystem
            MD5_FAILED,     ///< MD5 verification failed
            COMPLETE,       ///< Update complete
            ERROR          ///< Error occurred
        };

        /**
         * @brief Constructor
         * @param version Current firmware version
         * @param bucket Base URL for updates
         * @param fs Filesystem reference
         */
    AppUpdater(fs::FS& fs, Version localVersion, const char* bucket, const char* manifestName ="update.json", const char* appBin = "firmware.bin" );

    /**
     * @brief Change the base URL after construction (e.g. switch between MinIO and GCS)
     */
    void setBaseUrl(const String& newBaseUrl) { baseUrl = newBaseUrl; }

    /**
     * @brief Get the currently configured base URL
     */
    const String& getBaseUrl() const { return baseUrl; }

        /**
         * @brief Set progress callback function
         * @param callback Function to call with progress updates
         */
        void setProgressCallback(void (*callback)( UpdateStatus status, int percentage, const char* message));

        /**
         * @brief Check for and apply updates
         * @return true if update successful
         */
        bool IsUpdateAvailable(void);

        /**
         * @brief Update files from manifest
         * @param manifestPath Path to manifest file
         * @return true if all files updated successfully
         */
        //bool updateFilesFromManifest(const char* manifestPath = DEFAULT_MANIFEST_URL);
        bool updateFilesArray(void);

        /**
         * @brief Update single file
         * @param remotePath Remote file path
         * @param localPath Local file path
         * @param expectedMd5 Expected MD5 hash
         * @return true if file updated successfully
         */
        bool updateFile(const char* remotePath, const char* localPath, const char* expectedMd5);

        /**
         * @brief Get manifest content
         * @param manifestPath Path to manifest file
         * @return Manifest content as a json document
         */
    bool checkManifest(void);

        bool updateApp(void);

        String getVersion(void);

    private:
        typedef void (*ProgressCallback)(UpdateStatus status, int percentage, const char* message);
        ProgressCallback progressCb;
        fs::FS& fileSystem;
        UpdateStatus status;
        std::unique_ptr<uint8_t[]> downloadBuffer;
        bool updateAvailable = false;

        

        /**
         * @brief Verify and save file
         * @param stream Input stream
         * @param contentLength Expected content length
         * @param localPath Local file path
         * @param expectedMd5 Expected MD5 hash
         * @return true if successful
         */
    bool verifyAndSaveFile(WiFiClient* stream, size_t contentLength, 
                 const char* localPath, const char* expectedMd5);

        /**
         * @brief Update progress callback
         * @param percentage Progress percentage
         * @param newStatus Current status
         */
    void updateProgress(UpdateStatus newStatus, int percentage, const char* message = nullptr);

    /**
     * @brief Build a full URL by combining baseUrl and a relative path. Absolute (http/https) paths pass through.
     * Ensures exactly one slash joins base and path. Leading slash on path is stripped.
     */
    String buildUrl(const char* path) const;

        String getLocalMD5(const char* filePath);
};



// Queue handle for firmware update messages
extern QueueHandle_t updateMsgQueue;

// Message structure for update progress
struct UpdateMessage {
    String message;
    bool complete;
    int progress;
};

/**
 * @brief Firmware update task
 * @param param Task parameters
 */
void firmwareUpdateTask(void* param);

void startFirmwareUpdateTask(AsyncEventSource* eventSrc);

void loadUpdateJson(void);

void updateProgress(AppUpdater::UpdateStatus status, int percentage, const char* message);

void sendUpdateMessage(const char* message, bool complete, int progress);

void handleUpdateProgress(AsyncWebServerRequest *request);

bool checkManifest(Version& remoteVersion);

void startVersionCheckTask();

void versionCheckTask(void* parameter);