/*******************************************************************************
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 Accordion : public EditPlugin {
        private:
            // Header properties
            std::string headerLabel       = "Toggle Section";
            std::string headerBgColor     = "var(--blogi-surface)";
            std::string headerTextColor   = "var(--blogi-text)";
            std::string headerFontSize    = "16px";
            std::string headerFontWeight  = "bold";
            std::string headerPadding     = "12px 16px";
            std::string headerBorderRadius = "4px";

            // Content / body properties
            std::string contentBgColor    = "var(--blogi-bg)";
            std::string contentTextColor  = "var(--blogi-text)";
            std::string contentPadding    = "16px";
            std::string borderColor       = "var(--blogi-border)";
            std::string borderRadius      = "4px";

            // Light mode overrides
            std::string l_headerBgColor    = "";
            std::string l_headerTextColor  = "";
            std::string l_contentBgColor   = "";
            std::string l_contentTextColor = "";

            // Mobile overrides
            std::string m_headerFontSize   = "";
            std::string m_headerPadding    = "";
            std::string m_contentPadding   = "";

            // Behaviour
            bool openByDefault = false;
            std::string iconPosition = "right"; // "left", "right", "none"

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

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

        public:
            Accordion() {
                Type = "a2f7c1e3-49d8-4b5a-9c6e-8d1f3a7b2e04";
            }

            virtual ~Accordion() = default;

            const std::string getName() const override {
                return "Accordion";
            }

            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 << "AccordionPlugin Error (" << getInstanceId().c_str() << "): " << message << std::endl;
            }

            // --- UI Settings ---
            void Settings(libhttppp::HttpRequest &curreq, libhtmlpp::HtmlElement &settings) override {
                libhtmlpp::HtmlElement title("h3");
                title.appendChild(libhtmlpp::TextElement("Accordion Settings"));
                settings.appendChild(title);

                libhtmlpp::HtmlElement lblLabel("label");
                lblLabel.appendChild(libhtmlpp::TextElement("Header Label: "));
                settings.appendChild(lblLabel);
                libhtmlpp::HtmlElement lblInput("input");
                lblInput.setAttribute("type", "text");
                lblInput.setAttribute("name", "header_label");
                lblInput.setAttribute("value", headerLabel.c_str());
                settings.appendChild(lblInput);
            }

            // --- JSON API ---
            void JsonApi(json_object *request, json_object *response) override {
                if (request == nullptr) {
                    logError("Invalid JSON request received (nullptr).");
                    return;
                }

                json_object *action_obj = nullptr;
                const char *action = nullptr;

                if (json_object_object_get_ex(request, "action", &action_obj)) {
                    action = json_object_get_string(action_obj);
                }

                if (action == nullptr || action[0] == '\0') {
                    logError("No action specified in JsonApi request.");
                    return;
                }

                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_to_json_string_ext(props_obj, JSON_C_TO_STRING_NOSLASHESCAPE);
                        if (json_str) {
                            fromPropertiesJson(json_str);
                        }
                    }
                } else if (strcmp(action, "read") == 0) {
                    json_object *result_obj = json_object_new_object();
                    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("Accordion"));
                }
            }

            // --- 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 *visibleWhen = 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);
                    if (visibleWhen) json_object_object_add(f, "visibleWhen", visibleWhen);
                    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;
                };

                // Header settings
                addField("header_label", "Header Label", "text", "Toggle Section");
                addField("header_bg_color", "Header Background (Dark)", "color_var");
                addField("l_header_bg_color", "Header Background (Light)", "color_var", nullptr, "light");
                addField("header_text_color", "Header Text Color (Dark)", "color_var");
                addField("l_header_text_color", "Header Text Color (Light)", "color_var", nullptr, "light");
                addField("header_font_size", "Header Font Size", "text", "16px", "desktop");
                addField("header_font_weight", "Header Font Weight", "text", "bold");
                addField("header_padding", "Header Padding", "text", "12px 16px", "desktop");
                addField("header_border_radius", "Header Border Radius", "text", "4px");

                // Content settings
                addField("content_bg_color", "Content Background (Dark)", "color_var");
                addField("l_content_bg_color", "Content Background (Light)", "color_var", nullptr, "light");
                addField("content_text_color", "Content Text Color (Dark)", "color_var");
                addField("l_content_text_color", "Content Text Color (Light)", "color_var", nullptr, "light");
                addField("content_padding", "Content Padding", "text", "16px", "desktop");
                addField("border_color", "Border Color (Dark)", "color_var");
                addField("border_radius", "Border Radius", "text", "4px");

                // Behaviour
                addField("open_by_default", "Open by Default", "checkbox");
                addField("icon_position", "Toggle Icon Position", "select", nullptr, "all", mkOpts({"right", "left", "none"}));

                // Visibility
                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("header_label", headerLabel.c_str());
                element->SetAttribute("header_bg_color", headerBgColor.c_str());
                element->SetAttribute("header_text_color", headerTextColor.c_str());
                element->SetAttribute("header_font_size", headerFontSize.c_str());
                element->SetAttribute("header_font_weight", headerFontWeight.c_str());
                element->SetAttribute("header_padding", headerPadding.c_str());
                element->SetAttribute("header_border_radius", headerBorderRadius.c_str());

                element->SetAttribute("content_bg_color", contentBgColor.c_str());
                element->SetAttribute("content_text_color", contentTextColor.c_str());
                element->SetAttribute("content_padding", contentPadding.c_str());
                element->SetAttribute("border_color", borderColor.c_str());
                element->SetAttribute("border_radius", borderRadius.c_str());

                element->SetAttribute("l_header_bg_color", l_headerBgColor.c_str());
                element->SetAttribute("l_header_text_color", l_headerTextColor.c_str());
                element->SetAttribute("l_content_bg_color", l_contentBgColor.c_str());
                element->SetAttribute("l_content_text_color", l_contentTextColor.c_str());

                element->SetAttribute("m_header_font_size", m_headerFontSize.c_str());
                element->SetAttribute("m_header_padding", m_headerPadding.c_str());
                element->SetAttribute("m_content_padding", m_contentPadding.c_str());

                element->SetAttribute("open_by_default", openByDefault ? "true" : "false");
                element->SetAttribute("icon_position", iconPosition.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);
                }

                // Serialize child widgets
                EditPlugin *cur = child;
                while (cur) {
                    tinyxml2::XMLElement *childXml = cur->toXmlObject(doc);
                    if (childXml) element->InsertEndChild(childXml);
                    cur = const_cast<EditPlugin*>(cur->nextElement());
                }

                return element;
            }

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

                val = xml_data->Attribute("header_label");           if (val) headerLabel = val;
                val = xml_data->Attribute("header_bg_color");        if (val) headerBgColor = val;
                val = xml_data->Attribute("header_text_color");      if (val) headerTextColor = val;
                val = xml_data->Attribute("header_font_size");       if (val) headerFontSize = val;
                val = xml_data->Attribute("header_font_weight");     if (val) headerFontWeight = val;
                val = xml_data->Attribute("header_padding");         if (val) headerPadding = val;
                val = xml_data->Attribute("header_border_radius");   if (val) headerBorderRadius = val;

                val = xml_data->Attribute("content_bg_color");       if (val) contentBgColor = val;
                val = xml_data->Attribute("content_text_color");     if (val) contentTextColor = val;
                val = xml_data->Attribute("content_padding");        if (val) contentPadding = val;
                val = xml_data->Attribute("border_color");           if (val) borderColor = val;
                val = xml_data->Attribute("border_radius");          if (val) borderRadius = val;

                val = xml_data->Attribute("l_header_bg_color");      if (val) l_headerBgColor = val;
                val = xml_data->Attribute("l_header_text_color");    if (val) l_headerTextColor = val;
                val = xml_data->Attribute("l_content_bg_color");     if (val) l_contentBgColor = val;
                val = xml_data->Attribute("l_content_text_color");   if (val) l_contentTextColor = val;

                val = xml_data->Attribute("m_header_font_size");     if (val) m_headerFontSize = val;
                val = xml_data->Attribute("m_header_padding");       if (val) m_headerPadding = val;
                val = xml_data->Attribute("m_content_padding");      if (val) m_contentPadding = val;

                val = xml_data->Attribute("open_by_default");
                if (val) openByDefault = (strcmp(val, "true") == 0);
                val = xml_data->Attribute("icon_position");
                if (val) iconPosition = 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 Helpers ---
            std::string toPropertiesJson() {
                json_object *obj = json_object_new_object();

                json_object_object_add(obj, "header_label", json_object_new_string(headerLabel.c_str()));
                json_object_object_add(obj, "header_bg_color", json_object_new_string(headerBgColor.c_str()));
                json_object_object_add(obj, "header_text_color", json_object_new_string(headerTextColor.c_str()));
                json_object_object_add(obj, "header_font_size", json_object_new_string(headerFontSize.c_str()));
                json_object_object_add(obj, "header_font_weight", json_object_new_string(headerFontWeight.c_str()));
                json_object_object_add(obj, "header_padding", json_object_new_string(headerPadding.c_str()));
                json_object_object_add(obj, "header_border_radius", json_object_new_string(headerBorderRadius.c_str()));

                json_object_object_add(obj, "content_bg_color", json_object_new_string(contentBgColor.c_str()));
                json_object_object_add(obj, "content_text_color", json_object_new_string(contentTextColor.c_str()));
                json_object_object_add(obj, "content_padding", json_object_new_string(contentPadding.c_str()));
                json_object_object_add(obj, "border_color", json_object_new_string(borderColor.c_str()));
                json_object_object_add(obj, "border_radius", json_object_new_string(borderRadius.c_str()));

                json_object_object_add(obj, "l_header_bg_color", json_object_new_string(l_headerBgColor.c_str()));
                json_object_object_add(obj, "l_header_text_color", json_object_new_string(l_headerTextColor.c_str()));
                json_object_object_add(obj, "l_content_bg_color", json_object_new_string(l_contentBgColor.c_str()));
                json_object_object_add(obj, "l_content_text_color", json_object_new_string(l_contentTextColor.c_str()));

                json_object_object_add(obj, "m_header_font_size", json_object_new_string(m_headerFontSize.c_str()));
                json_object_object_add(obj, "m_header_padding", json_object_new_string(m_headerPadding.c_str()));
                json_object_object_add(obj, "m_content_padding", json_object_new_string(m_contentPadding.c_str()));

                json_object_object_add(obj, "open_by_default", json_object_new_boolean(openByDefault));
                json_object_object_add(obj, "icon_position", json_object_new_string(iconPosition.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, "header_label", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) headerLabel = val;
                }
                if (json_object_object_get_ex(obj, "header_bg_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) headerBgColor = val;
                }
                if (json_object_object_get_ex(obj, "header_text_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) headerTextColor = val;
                }
                if (json_object_object_get_ex(obj, "header_font_size", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) headerFontSize = val;
                }
                if (json_object_object_get_ex(obj, "header_font_weight", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) headerFontWeight = val;
                }
                if (json_object_object_get_ex(obj, "header_padding", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) headerPadding = val;
                }
                if (json_object_object_get_ex(obj, "header_border_radius", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) headerBorderRadius = val;
                }
                if (json_object_object_get_ex(obj, "content_bg_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) contentBgColor = val;
                }
                if (json_object_object_get_ex(obj, "content_text_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) contentTextColor = val;
                }
                if (json_object_object_get_ex(obj, "content_padding", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) contentPadding = val;
                }
                if (json_object_object_get_ex(obj, "border_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) borderColor = val;
                }
                if (json_object_object_get_ex(obj, "border_radius", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) borderRadius = val;
                }
                if (json_object_object_get_ex(obj, "l_header_bg_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) l_headerBgColor = val;
                }
                if (json_object_object_get_ex(obj, "l_header_text_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) l_headerTextColor = val;
                }
                if (json_object_object_get_ex(obj, "l_content_bg_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) l_contentBgColor = val;
                }
                if (json_object_object_get_ex(obj, "l_content_text_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) l_contentTextColor = val;
                }
                if (json_object_object_get_ex(obj, "m_header_font_size", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_headerFontSize = val;
                }
                if (json_object_object_get_ex(obj, "m_header_padding", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_headerPadding = val;
                }
                if (json_object_object_get_ex(obj, "m_content_padding", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_contentPadding = val;
                }
                if (json_object_object_get_ex(obj, "open_by_default", &val_obj)) {
                    if (json_object_is_type(val_obj, json_type_string)) {
                        const char *s = json_object_get_string(val_obj);
                        openByDefault = (s && (strcmp(s, "true") == 0 || strcmp(s, "1") == 0));
                    } else {
                        openByDefault = json_object_get_boolean(val_obj);
                    }
                }
                if (json_object_object_get_ex(obj, "icon_position", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) iconPosition = val;
                }
                if (json_object_object_get_ex(obj, "hidden", &val_obj)) {
                    if (json_object_is_type(val_obj, json_type_string)) {
                        const char *s = json_object_get_string(val_obj);
                        hidden = (s && (strcmp(s, "true") == 0 || strcmp(s, "1") == 0));
                    } else {
                        hidden = json_object_get_boolean(val_obj);
                    }
                }
                if (json_object_object_get_ex(obj, "m_hidden", &val_obj)) {
                    if (json_object_is_type(val_obj, json_type_string)) {
                        const char *s = json_object_get_string(val_obj);
                        m_hidden = (s && (strcmp(s, "true") == 0 || strcmp(s, "1") == 0));
                    } else {
                        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 instanceId(getInstanceId().c_str());
                std::string rc = std::string("we-") + instanceId;
                std::string contentId = "acc-content-" + instanceId;

                auto validCss = [](const std::string &v) {
                    return !v.empty() && v != "none";
                };

                // --- Wrapper ---
                libhtmlpp::HtmlElement wrapper("div");
                wrapper.setAttribute("class", (rc + "-acc").c_str());

                std::string wrapperStyle;
                if (validCss(borderColor)) wrapperStyle += "border: 1px solid " + borderColor + ";";
                if (validCss(borderRadius)) wrapperStyle += " border-radius: " + borderRadius + ";";
                wrapperStyle += " overflow: hidden;";
                if (hidden) wrapperStyle += " display: none;";
                wrapper.setAttribute("style", wrapperStyle.c_str());

                // --- Header / Toggle Button ---
                libhtmlpp::HtmlElement header("button");
                header.setAttribute("type", "button");
                header.setAttribute("class", (rc + "-hdr").c_str());

                std::string hdrStyle;
                hdrStyle += "display: flex; align-items: center; width: 100%;";
                hdrStyle += " cursor: pointer; border: none;";
                if (iconPosition == "left") hdrStyle += " flex-direction: row;";
                else hdrStyle += " flex-direction: row;";
                hdrStyle += " justify-content: space-between;";
                if (validCss(headerBgColor))    hdrStyle += " background-color: " + headerBgColor + ";";
                else                            hdrStyle += " background-color: transparent;";
                if (validCss(headerTextColor))   hdrStyle += " color: " + headerTextColor + ";";
                if (validCss(headerFontSize))    hdrStyle += " font-size: " + headerFontSize + ";";
                if (validCss(headerFontWeight))  hdrStyle += " font-weight: " + headerFontWeight + ";";
                if (validCss(headerPadding))     hdrStyle += " padding: " + headerPadding + ";";
                header.setAttribute("style", hdrStyle.c_str());

                // Toggle JS
                std::string toggleJs =
                    "var c=document.getElementById('" + contentId + "');"
                    "var icon=this.querySelector('.acc-icon');"
                    "if(c.style.maxHeight&&c.style.maxHeight!=='0px'){"
                    "c.style.maxHeight='0px';c.style.opacity='0';"
                    "if(icon)icon.style.transform='rotate(0deg)';"
                    "}else{"
                    "c.style.maxHeight=c.scrollHeight+'px';c.style.opacity='1';"
                    "if(icon)icon.style.transform='rotate(180deg)';"
                    "}";
                header.setAttribute("onclick", toggleJs.c_str());

                // Label span
                libhtmlpp::HtmlElement labelSpan("span");
                labelSpan.appendChild(libhtmlpp::TextElement(headerLabel.c_str()));

                // Icon span (chevron ▾)
                libhtmlpp::HtmlElement iconSpan("span");
                iconSpan.setAttribute("class", "acc-icon");
                std::string iconStyle = "transition: transform 0.25s ease; display: inline-block;";
                if (openByDefault) iconStyle += " transform: rotate(180deg);";
                iconSpan.setAttribute("style", iconStyle.c_str());
                iconSpan.appendChild(libhtmlpp::TextElement("\xe2\x96\xbe")); // ▾

                if (iconPosition == "left") {
                    header.appendChild(iconSpan);
                    header.appendChild(labelSpan);
                } else if (iconPosition == "none") {
                    header.appendChild(labelSpan);
                } else {
                    header.appendChild(labelSpan);
                    header.appendChild(iconSpan);
                }

                wrapper.appendChild(header);

                // --- Content Panel ---
                libhtmlpp::HtmlElement content("div");
                content.setAttribute("id", contentId.c_str());
                content.setAttribute("class", (rc + "-body").c_str());

                std::string cStyle;
                cStyle += "overflow: hidden; transition: max-height 0.3s ease, opacity 0.3s ease;";
                if (openByDefault) {
                    cStyle += " opacity: 1;";
                    // max-height will be set by JS on first interaction; start large
                } else {
                    cStyle += " max-height: 0px; opacity: 0;";
                }
                if (validCss(contentBgColor))   cStyle += " background: " + contentBgColor + ";";
                if (validCss(contentTextColor))  cStyle += " color: " + contentTextColor + ";";
                if (validCss(contentPadding))    cStyle += " padding: " + contentPadding + ";";
                content.setAttribute("style", cStyle.c_str());

                // Render child widgets into content
                const EditPlugin *cur = child;
                while (cur) {
                    cur->Render(content);
                    cur = cur->nextElement();
                }

                wrapper.appendChild(content);
                el.appendChild(wrapper);

                // --- Auto-expand for openByDefault ---
                if (openByDefault) {
                    std::string initScript =
                        "(function(){"
                        "var c=document.getElementById('" + contentId + "');"
                        "if(c)c.style.maxHeight=c.scrollHeight+'px';"
                        "})();";
                    libhtmlpp::HtmlElement script("script");
                    script.appendChild(libhtmlpp::TextElement(initScript.c_str()));
                    el.appendChild(script);
                }

                // --- Responsive & Light mode CSS ---
                std::string mediaCSS;

                // Mobile header overrides
                std::string mobileHdrCSS;
                if (validCss(m_headerFontSize)) mobileHdrCSS += " font-size: " + m_headerFontSize + " !important;";
                if (validCss(m_headerPadding))  mobileHdrCSS += " padding: " + m_headerPadding + " !important;";
                if (m_hidden) mobileHdrCSS += " display: none !important;";

                // Mobile content overrides
                std::string mobileBodyCSS;
                if (validCss(m_contentPadding)) mobileBodyCSS += " padding: " + m_contentPadding + " !important;";

                // Mobile wrapper visibility
                std::string mobileWrapCSS;
                if (m_hidden) mobileWrapCSS += " display: none !important;";
                else if (hidden) mobileWrapCSS += " display: block !important;";

                if (!mobileHdrCSS.empty() || !mobileBodyCSS.empty() || !mobileWrapCSS.empty()) {
                    mediaCSS += "@media (max-width: 768px) {";
                    if (!mobileWrapCSS.empty())
                        mediaCSS += " ." + rc + "-acc {" + mobileWrapCSS + " }";
                    if (!mobileHdrCSS.empty())
                        mediaCSS += " ." + rc + "-hdr {" + mobileHdrCSS + " }";
                    if (!mobileBodyCSS.empty())
                        mediaCSS += " ." + rc + "-body {" + mobileBodyCSS + " }";
                    mediaCSS += " } ";
                }

                // Light mode overrides
                std::string lightHdrCSS;
                if (validCss(l_headerBgColor))   lightHdrCSS += " background-color: " + l_headerBgColor + " !important;";
                if (validCss(l_headerTextColor)) lightHdrCSS += " color: " + l_headerTextColor + " !important;";

                std::string lightBodyCSS;
                if (validCss(l_contentBgColor))   lightBodyCSS += " background: " + l_contentBgColor + " !important;";
                if (validCss(l_contentTextColor)) lightBodyCSS += " color: " + l_contentTextColor + " !important;";

                if (!lightHdrCSS.empty() || !lightBodyCSS.empty()) {
                    mediaCSS += "@media (prefers-color-scheme: light) {";
                    if (!lightHdrCSS.empty())
                        mediaCSS += " ." + rc + "-hdr {" + lightHdrCSS + " }";
                    if (!lightBodyCSS.empty())
                        mediaCSS += " ." + rc + "-body {" + lightBodyCSS + " }";
                    mediaCSS += " }";
                }

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

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

            // --- Element Management (Accordion is a container) ---
            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::Accordion* create() {
    return new blogi::webedit::Accordion();
}

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