/*******************************************************************************
Copyright (c) 2025, Jan Koester jan.koester@gmx.net
All rights reserved.

Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
    * Redistributions of source code must retain the above copyright
      notice, this list of conditions and the following disclaimer.
    * Redistributions in binary form must reproduce the above copyright
      notice, this list of conditions and the following disclaimer in the
      documentation and/or other materials provided with the distribution.
    * Neither the name of the <organization> nor the
      names of its contributors may be used to endorse or promote products
      derived from this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************/

#include <sstream>
#include <iostream>
#include <cstring>

#include <tinyxml2.h>
#include <htmlpp/html.h>

#include "plugin.h"

extern "C" {
    namespace blogi::webedit {

        class Slider : public EditPlugin {
        private:
            // Slider properties
            std::string width           = "100%";
            std::string height          = "400px";
            std::string backgroundColor = "";
            std::string borderRadius    = "0";
            std::string transition      = "fade";    // fade, slide
            std::string interval        = "5000";    // ms between slides
            bool        autoplay        = true;
            bool        showArrows      = true;
            bool        showDots        = true;

            // Light mode overrides
            std::string l_backgroundColor = "";

            // Mobile overrides
            std::string m_width   = "";
            std::string m_height  = "";

            // Visibility
            bool hidden   = false;
            bool m_hidden = false;

            // Custom CSS
            std::string customCss = "";

        public:
            Slider() {
                Type = "ba0937c1-fbb2-44cf-b622-6a50bdfc3161";
            }

            ~Slider() override = default;

            const std::string getName() const override { return "Slider"; }
            const std::string getAuthor() const override { return "Jan Koester"; }
            const std::string getVersion() const override { return "1.0"; }

            void logError(const std::string& message) override {
                std::cerr << "[Slider] " << message << std::endl;
            }

            // --- Settings UI ---
            void Settings(libhttppp::HttpRequest &curreq, libhtmlpp::HtmlElement &settings) override {
            }

            // --- JSON API ---
            void JsonApi(json_object *request, json_object *response) override {
                if (!request || !response) return;

                json_object *action_obj = nullptr;
                if (!json_object_object_get_ex(request, "action", &action_obj)) return;
                const char *action = json_object_get_string(action_obj);
                if (!action) return;

                if (strcmp(action, "add") == 0) {
                    json_object_object_add(response, "status", json_object_new_string("success"));
                    json_object_object_add(response, "plugin_type", json_object_new_string("Slider"));
                }
                else if (strcmp(action, "delete") == 0) {
                    json_object_object_add(response, "status", json_object_new_string("success"));
                    json_object_object_add(response, "plugin_type", json_object_new_string("Slider"));
                }
                else if (strcmp(action, "modify_bulk") == 0) {
                    json_object *props_obj = nullptr;
                    if (json_object_object_get_ex(request, "properties", &props_obj)) {
                        const char *json_str = json_object_get_string(props_obj);
                        if (json_str) {
                            fromPropertiesJson(json_str);
                        }
                    }
                    json_object_object_add(response, "status", json_object_new_string("success"));
                    json_object_object_add(response, "plugin_type", json_object_new_string("Slider"));
                }
                else if (strcmp(action, "read") == 0) {
                    json_object *result_obj = json_object_new_object();
                    json_object_object_add(result_obj, "plugin_type", json_object_new_string("Slider"));
                    json_object_object_add(result_obj, "instance_id", json_object_new_string(getInstanceId().c_str()));
                    json_object_object_add(result_obj, "type_id", json_object_new_string(getTypeId().c_str()));
                    json_object_object_add(result_obj, "properties", json_object_new_string(toPropertiesJson().c_str()));
                    if (response) {
                        json_object_object_add(response, "data", result_obj);
                    }
                }
                else if (strcmp(action, "get_schema") == 0) {
                    if (response) {
                        json_object_object_add(response, "schema", getSchemaJson());
                    }
                }

                if (response != nullptr) {
                    json_object_object_add(response, "status", json_object_new_string("success"));
                    json_object_object_add(response, "plugin_type", json_object_new_string("Slider"));
                }
            }

            // --- Schema ---
            static json_object* getSchemaJson() {
                json_object *arr = json_object_new_array();
                auto addField = [&](const char *key, const char *label, const char *type,
                                    const char *placeholder = nullptr, const char *target = "all",
                                    json_object *options = nullptr) {
                    json_object *f = json_object_new_object();
                    json_object_object_add(f, "key", json_object_new_string(key));
                    json_object_object_add(f, "label", json_object_new_string(label));
                    json_object_object_add(f, "type", json_object_new_string(type));
                    if (placeholder) json_object_object_add(f, "placeholder", json_object_new_string(placeholder));
                    json_object_object_add(f, "target", json_object_new_string(target));
                    if (options) json_object_object_add(f, "options", options);
                    json_object_array_add(arr, f);
                };
                auto mkOpts = [](std::initializer_list<const char*> vals) {
                    json_object *a = json_object_new_array();
                    for (auto v : vals) json_object_array_add(a, json_object_new_string(v));
                    return a;
                };
                addField("width", "Width", "text", "e.g. 100%", "desktop");
                addField("height", "Height", "text", "e.g. 400px", "desktop");
                addField("background_color", "Background Color (Dark)", "color_var", nullptr, "desktop");
                addField("l_background_color", "Background Color (Light)", "color_var", nullptr, "light");
                addField("border_radius", "Border Radius", "text", "e.g. 8px");
                addField("transition", "Transition", "select", nullptr, "all", mkOpts({"fade","slide"}));
                addField("interval", "Interval (ms)", "text", "e.g. 5000");
                addField("autoplay", "Autoplay", "checkbox");
                addField("show_arrows", "Show Arrows", "checkbox");
                addField("show_dots", "Show Dots", "checkbox");
                addField("hidden", "Hidden", "checkbox", nullptr, "desktop");
                {
                    json_object *hf = json_object_array_get_idx(arr, json_object_array_length(arr) - 1);
                    json_object_object_add(hf, "default", json_object_new_boolean(false));
                }
                addField("custom_css", "Custom CSS", "textarea", ".we-xxx { ... }");
                return arr;
            }

            // --- XML Persistence ---

            tinyxml2::XMLElement* toXmlObject(tinyxml2::XMLDocument* doc) override {
                tinyxml2::XMLElement* element = doc->NewElement(getName().c_str());
                element->SetAttribute("instance_id", getInstanceId().c_str());
                element->SetAttribute("type_id", getTypeId().c_str());

                element->SetAttribute("width", width.c_str());
                element->SetAttribute("height", height.c_str());
                element->SetAttribute("background_color", backgroundColor.c_str());
                element->SetAttribute("border_radius", borderRadius.c_str());
                element->SetAttribute("transition", transition.c_str());
                element->SetAttribute("interval", interval.c_str());
                element->SetAttribute("autoplay", autoplay ? "true" : "false");
                element->SetAttribute("show_arrows", showArrows ? "true" : "false");
                element->SetAttribute("show_dots", showDots ? "true" : "false");

                element->SetAttribute("l_background_color", l_backgroundColor.c_str());

                element->SetAttribute("m_width", m_width.c_str());
                element->SetAttribute("m_height", m_height.c_str());

                element->SetAttribute("hidden", hidden ? "true" : "false");
                element->SetAttribute("m_hidden", m_hidden ? "true" : "false");

                if (!customCss.empty()) {
                    tinyxml2::XMLElement* cssEl = doc->NewElement("CustomCss");
                    cssEl->SetText(customCss.c_str());
                    element->InsertEndChild(cssEl);
                }

                const EditPlugin *cur = child;
                while (cur) {
                    element->InsertEndChild(const_cast<EditPlugin*>(cur)->toXmlObject(doc));
                    cur = cur->nextElement();
                }

                return element;
            }

            void fromXmlObject(tinyxml2::XMLElement* xml_data) override {
                const char* val = nullptr;

                val = xml_data->Attribute("width");
                if (val) width = val;
                val = xml_data->Attribute("height");
                if (val) height = val;
                val = xml_data->Attribute("background_color");
                if (val) backgroundColor = val;
                val = xml_data->Attribute("border_radius");
                if (val) borderRadius = val;
                val = xml_data->Attribute("transition");
                if (val) transition = val;
                val = xml_data->Attribute("interval");
                if (val) interval = val;

                val = xml_data->Attribute("autoplay");
                if (val) autoplay = (strcmp(val, "true") == 0);
                val = xml_data->Attribute("show_arrows");
                if (val) showArrows = (strcmp(val, "true") == 0);
                val = xml_data->Attribute("show_dots");
                if (val) showDots = (strcmp(val, "true") == 0);

                val = xml_data->Attribute("l_background_color");
                if (val) l_backgroundColor = val;

                val = xml_data->Attribute("m_width");
                if (val) m_width = val;
                val = xml_data->Attribute("m_height");
                if (val) m_height = val;

                val = xml_data->Attribute("hidden");
                if (val) hidden = (strcmp(val, "true") == 0);
                val = xml_data->Attribute("m_hidden");
                if (val) m_hidden = (strcmp(val, "true") == 0);

                tinyxml2::XMLElement* cssEl = xml_data->FirstChildElement("CustomCss");
                if (cssEl) {
                    const char* cssVal = cssEl->GetText();
                    if (cssVal) customCss = cssVal;
                }
            }

            // --- JSON Properties ---

            std::string toPropertiesJson() {
                json_object *obj = json_object_new_object();
                json_object_object_add(obj, "width", json_object_new_string(width.c_str()));
                json_object_object_add(obj, "height", json_object_new_string(height.c_str()));
                json_object_object_add(obj, "background_color", json_object_new_string(backgroundColor.c_str()));
                json_object_object_add(obj, "border_radius", json_object_new_string(borderRadius.c_str()));
                json_object_object_add(obj, "transition", json_object_new_string(transition.c_str()));
                json_object_object_add(obj, "interval", json_object_new_string(interval.c_str()));
                json_object_object_add(obj, "autoplay", json_object_new_boolean(autoplay));
                json_object_object_add(obj, "show_arrows", json_object_new_boolean(showArrows));
                json_object_object_add(obj, "show_dots", json_object_new_boolean(showDots));
                json_object_object_add(obj, "l_background_color", json_object_new_string(l_backgroundColor.c_str()));
                json_object_object_add(obj, "m_width", json_object_new_string(m_width.c_str()));
                json_object_object_add(obj, "m_height", json_object_new_string(m_height.c_str()));
                json_object_object_add(obj, "hidden", json_object_new_boolean(hidden));
                json_object_object_add(obj, "m_hidden", json_object_new_boolean(m_hidden));
                json_object_object_add(obj, "custom_css", json_object_new_string(customCss.c_str()));
                std::string result = json_object_to_json_string_ext(obj, JSON_C_TO_STRING_NOSLASHESCAPE);
                json_object_put(obj);
                return result;
            }

            void fromPropertiesJson(const std::string &json) {
                json_object *obj = json_tokener_parse(json.c_str());
                if (!obj) return;

                json_object *val_obj = nullptr;
                const char *val = nullptr;

                if (json_object_object_get_ex(obj, "width", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) width = val;
                }
                if (json_object_object_get_ex(obj, "height", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) height = val;
                }
                if (json_object_object_get_ex(obj, "background_color", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) backgroundColor = val;
                }
                if (json_object_object_get_ex(obj, "border_radius", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) borderRadius = val;
                }
                if (json_object_object_get_ex(obj, "transition", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) transition = val;
                }
                if (json_object_object_get_ex(obj, "interval", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) interval = val;
                }
                if (json_object_object_get_ex(obj, "autoplay", &val_obj)) {
                    autoplay = json_object_get_boolean(val_obj);
                }
                if (json_object_object_get_ex(obj, "show_arrows", &val_obj)) {
                    showArrows = json_object_get_boolean(val_obj);
                }
                if (json_object_object_get_ex(obj, "show_dots", &val_obj)) {
                    showDots = json_object_get_boolean(val_obj);
                }
                if (json_object_object_get_ex(obj, "l_background_color", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) l_backgroundColor = val;
                }
                if (json_object_object_get_ex(obj, "m_width", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) m_width = val;
                }
                if (json_object_object_get_ex(obj, "m_height", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) m_height = val;
                }
                if (json_object_object_get_ex(obj, "hidden", &val_obj)) {
                    hidden = json_object_get_boolean(val_obj);
                }
                if (json_object_object_get_ex(obj, "m_hidden", &val_obj)) {
                    m_hidden = json_object_get_boolean(val_obj);
                }
                if (json_object_object_get_ex(obj, "custom_css", &val_obj)) {
                    val = json_object_get_string(val_obj);
                    if (val) customCss = val;
                }

                json_object_put(obj);
            }

            // --- HTML Rendering ---

            void Render(libhtmlpp::HtmlElement &el) const override {
                std::string cls = std::string("we-") + getInstanceId().c_str();
                std::string sliderId = std::string("slider-") + getInstanceId().c_str();

                // Count children for dot navigation
                int slideCount = 0;
                const EditPlugin *counter = child;
                while (counter) {
                    slideCount++;
                    counter = counter->nextElement();
                }

                // --- Slider container styles ---
                std::string style = "position: relative; overflow: hidden; touch-action: pan-y pinch-zoom;";
                if (!width.empty()) style += " width: " + width + ";";
                if (!height.empty()) style += " min-height: " + height + ";";
                if (!backgroundColor.empty() && backgroundColor != "none") {
                    style += " background-color: " + backgroundColor + ";";
                }
                if (!borderRadius.empty() && borderRadius != "0") {
                    style += " border-radius: " + borderRadius + ";";
                }
                if (hidden) style += " display: none;";

                libhtmlpp::HtmlElement wrapper("div");
                wrapper.setAttribute("class", cls.c_str());
                wrapper.setAttribute("id", sliderId.c_str());
                wrapper.setAttribute("style", style.c_str());

                // --- Slide track ---
                std::string trackStyle;
                if (transition == "fade") {
                    // Grid stacking: all slides in same cell, tallest determines height
                    trackStyle = "display: grid; grid-template-columns: 1fr; grid-template-rows: 1fr; touch-action: pan-y;";
                } else {
                    trackStyle = "display: flex; height: 100%; touch-action: pan-y; flex-wrap: nowrap; will-change: transform;";
                    trackStyle += " transition: transform 0.5s ease-in-out;";
                }

                libhtmlpp::HtmlElement track("div");
                std::string trackCls = cls + "-track";
                track.setAttribute("class", trackCls.c_str());
                track.setAttribute("style", trackStyle.c_str());

                // Render each child as a slide
                const EditPlugin *cur = child;
                while (cur) {
                    libhtmlpp::HtmlElement slide("div");
                    std::string slideStyle;
                    if (transition == "fade") {
                        // All slides stack in grid row 1 / col 1; tallest sets the height
                        slideStyle = "grid-row: 1; grid-column: 1; width: 100%;"
                                     " opacity: 0; transition: opacity 0.5s ease-in-out;"
                                     " -webkit-user-drag: none; touch-action: pan-y; user-select: none; box-sizing: border-box;";
                    } else {
                        slideStyle = "width: 100%; flex: 0 0 100%; height: 100%; position: relative;"
                                     " overflow-x: hidden; -webkit-user-drag: none; touch-action: pan-y; user-select: none; box-sizing: border-box;";
                    }
                    slide.setAttribute("style", slideStyle.c_str());
                    slide.setAttribute("class", (cls + "-slide").c_str());
                    cur->Render(slide);
                    track.appendChild(slide);
                    cur = cur->nextElement();
                }

                wrapper.appendChild(track);

                // --- Navigation Arrows ---
                if (showArrows && slideCount > 1) {
                    std::string arrowBase = "position: absolute; top: 50%; transform: translateY(-50%);"
                        " background: rgba(0,0,0,0.5); color: white; border: none; padding: 12px 16px;"
                        " font-size: 18px; cursor: pointer; z-index: 10; border-radius: 4px;";

                    libhtmlpp::HtmlElement prevBtn("button");
                    prevBtn.setAttribute("style", (arrowBase + " left: 10px;").c_str());
                    prevBtn.setAttribute("onclick",
                        (std::string("(function(){var s=document.getElementById('") + sliderId +
                         "');s._blogiSlider&&s._blogiSlider.prev()})()").c_str());
                    prevBtn.appendChild(libhtmlpp::TextElement("&#10094;"));
                    wrapper.appendChild(prevBtn);

                    libhtmlpp::HtmlElement nextBtn("button");
                    nextBtn.setAttribute("style", (arrowBase + " right: 10px;").c_str());
                    nextBtn.setAttribute("onclick",
                        (std::string("(function(){var s=document.getElementById('") + sliderId +
                         "');s._blogiSlider&&s._blogiSlider.next()})()").c_str());
                    nextBtn.appendChild(libhtmlpp::TextElement("&#10095;"));
                    wrapper.appendChild(nextBtn);
                }

                // --- Dot Navigation ---
                if (showDots && slideCount > 1) {
                    libhtmlpp::HtmlElement dots("div");
                    dots.setAttribute("class", (cls + "-dots").c_str());
                    dots.setAttribute("style",
                        "position: absolute; bottom: 12px; left: 50%; transform: translateX(-50%);"
                        " display: flex; gap: 8px; z-index: 10;");

                    for (int i = 0; i < slideCount; i++) {
                        libhtmlpp::HtmlElement dot("button");
                        dot.setAttribute("style",
                            "width: 12px; height: 12px; border-radius: 50%;"
                            " border: 2px solid white; background: rgba(255,255,255,0.4);"
                            " cursor: pointer; padding: 0;");
                        dot.setAttribute("onclick",
                            (std::string("(function(){var s=document.getElementById('") + sliderId +
                             "');s._blogiSlider&&s._blogiSlider.goTo(" + std::to_string(i) + ")})()").c_str());
                        dots.appendChild(dot);
                    }
                    wrapper.appendChild(dots);
                }

                el.appendChild(wrapper);

                // --- Inline slider script ---
                if (slideCount > 0) {
                    libhtmlpp::HtmlElement script("script");
                    std::stringstream js;
                    js << "(function(){";
                    js << "var el=document.getElementById('" << sliderId << "');";
                    js << "if(!el)return;";
                    js << "var slides=el.querySelectorAll('." << cls << "-slide');";
                    js << "var dots=el.querySelectorAll('." << cls << "-dots button');";
                    js << "var n=slides.length,cur=0,timer=null;";
                    if (transition == "fade") {
                        // Fade mode uses CSS grid stacking — no size override needed
                        js << "function setSizes(){}";
                    } else {
                        // Set slide widths in pixels for reliable mobile rendering
                        js << "function setSizes(){var w=el.offsetWidth;for(var j=0;j<n;j++){slides[j].style.width=w+'px';slides[j].style.flex='0 0 '+w+'px';}}";
                    }
                    js << "setSizes();";
                    js << "function show(i){";
                    js << "  cur=((i%n)+n)%n;";
                    if (transition == "fade") {
                        js << "  for(var j=0;j<n;j++){slides[j].style.opacity=j===cur?'1':'0';}";
                    } else {
                        js << "  var track=el.querySelector('." << trackCls << "');";
                        js << "  if(track)track.style.transform='translateX(-'+(cur*el.offsetWidth)+'px)';";
                    }
                    js << "  for(var j=0;j<dots.length;j++){";
                    js << "    dots[j].style.background=j===cur?'white':'rgba(255,255,255,0.4)';";
                    js << "  }";
                    js << "}";
                    js << "function next(){show(cur+1);}";
                    js << "function prev(){show(cur-1);}";
                    js << "function goTo(i){show(i);}";
                    js << "function stopAuto(){if(timer){clearInterval(timer);timer=null;}}";
                    js << "function startAuto(){";
                    js << "  if(timer)clearInterval(timer);";
                    js << "  timer=setInterval(next," << interval << ");";
                    js << "}";
                    js << "el.addEventListener('click',function(){stopAuto();});";
                    js << "show(0);";
                    if (autoplay && slideCount > 1) {
                        js << "startAuto();";
                    }
                    // Recalculate sizes and position on resize/orientation change
                    js << "function onLayout(){setSizes();show(cur);}";
                    js << "window.addEventListener('resize',onLayout);";
                    js << "if(screen&&screen.orientation)screen.orientation.addEventListener('change',function(){setTimeout(onLayout,100);});";
                    js << "else window.addEventListener('orientationchange',function(){setTimeout(onLayout,200);});";
                    // Touch/swipe support for mobile
                    js << "el.querySelectorAll('img,video,a,div,span,p,h1,h2,h3,h4,button').forEach(function(m){m.style.touchAction='pan-y';m.draggable=false;m.style.webkitUserDrag='none';m.style.userSelect='none';});";
                    js << "var tx=0,ty=0,moving=false;";
                    js << "el.addEventListener('touchstart',function(e){";
                    js << "  tx=e.touches[0].clientX;ty=e.touches[0].clientY;moving=true;";
                    js << "},{passive:true});";
                    js << "el.addEventListener('touchmove',function(e){";
                    js << "  if(!moving)return;";
                    js << "  var dx=e.touches[0].clientX-tx,dy=e.touches[0].clientY-ty;";
                    js << "  if(Math.abs(dx)>Math.abs(dy)&&Math.abs(dx)>10)e.preventDefault();";
                    js << "},{passive:false});";
                    js << "el.addEventListener('touchend',function(e){";
                    js << "  if(!moving)return;moving=false;";
                    js << "  var dx=e.changedTouches[0].clientX-tx;";
                    js << "  var dy=e.changedTouches[0].clientY-ty;";
                    js << "  if(Math.abs(dx)>Math.abs(dy)&&Math.abs(dx)>30){";
                    js << "    if(dx<0)next();else prev();";
                    js << "    stopAuto();";
                    js << "  }";
                    js << "},{passive:true});";
                    js << "el.addEventListener('touchcancel',function(){moving=false;},{passive:true});";
                    js << "el._blogiSlider={next:next,prev:prev,goTo:goTo,stopAuto:stopAuto,startAuto:startAuto};";
                    js << "})();";
                    script.appendChild(libhtmlpp::TextElement(js.str().c_str()));
                    el.appendChild(script);
                }

                // --- Responsive CSS ---
                std::string mobileCSS;
                if (!m_width.empty()) mobileCSS += " width: " + m_width + " !important;";
                if (!m_height.empty()) mobileCSS += " height: " + m_height + " !important;";
                if (m_hidden) mobileCSS += " display: none !important;";
                else if (hidden) mobileCSS += " display: block !important;";

                if (!mobileCSS.empty()) {
                    libhtmlpp::HtmlElement styleTag("style");
                    std::string mediaRule = "@media (max-width: 768px) { ." + cls + " {" + mobileCSS + " } }";
                    styleTag.appendChild(libhtmlpp::TextElement(mediaRule.c_str()));
                    el.appendChild(styleTag);
                }

                // Light mode overrides
                std::string lightCSS;
                if (!l_backgroundColor.empty() && l_backgroundColor != "none") {
                    lightCSS += " background-color: " + l_backgroundColor + " !important;";
                }
                if (!lightCSS.empty()) {
                    libhtmlpp::HtmlElement lightStyle("style");
                    std::string lightRule = "@media (prefers-color-scheme: light) { ." + cls + " {" + lightCSS + " } }";
                    lightStyle.appendChild(libhtmlpp::TextElement(lightRule.c_str()));
                    el.appendChild(lightStyle);
                }

                if (!customCss.empty()) {
                    libhtmlpp::HtmlElement cssStyle("style");
                    cssStyle.appendChild(libhtmlpp::TextElement(customCss.c_str()));
                    el.appendChild(cssStyle);
                }
            }

            // --- Element Management (Container — can have children as slides) ---

            bool canHaveChildren() const override { return true; }

            bool haveChilds() override { return child != nullptr; }

            void setChildElement(const EditPlugin &el) override {
                child = const_cast<EditPlugin*>(&el);
            }

            void unsetChildElement(const EditPlugin &el) override {
                if (child == &el) {
                    child = const_cast<EditPlugin*>(el.nextElement());
                }
            }

            void addNextElement(const EditPlugin &el) override {
                EditPlugin *newEl = const_cast<EditPlugin*>(&el);

                if (!child) {
                    setChildElement(el);
                    return;
                }

                EditPlugin *cur = child;
                while (cur->nextElement() != nullptr) {
                    cur = const_cast<EditPlugin*>(cur->nextElement());
                }
                cur->setNextEl(newEl);
            }

            void remNextElement(const EditPlugin &el) override {
                if (!child) return;

                if (child == &el) {
                    unsetChildElement(el);
                    return;
                }

                EditPlugin *cur = child;
                while (cur->nextElement() != nullptr) {
                    if (cur->nextElement() == &el) {
                        cur->setNextEl(const_cast<EditPlugin*>(el.nextElement()));
                        return;
                    }
                    cur = const_cast<EditPlugin*>(cur->nextElement());
                }
            }
        };
    };
}

// --- Factory Functions ---

EXPORT blogi::webedit::Slider* create() {
    return new blogi::webedit::Slider();
}

EXPORT void destroy(blogi::webedit::Slider* p) {
    delete p;
}
