/*******************************************************************************
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 Button : public EditPlugin {
        private:
            std::string label           = "Click Me";
            std::string linkUrl         = "#";
            std::string linkType        = "url";    // "url" or "section"
            std::string sectionId       = "";       // target section instance id
            std::string backgroundColor = "var(--blogi-accent)";
            std::string textColor       = "var(--blogi-text)";
            std::string fontSize        = "16px";
            std::string padding         = "10px 20px";
            std::string borderRadius    = "4px";
            std::string borderStyle     = "none";
            std::string textAlign       = "center";
            std::string width           = "";
            std::string height          = "";
            bool openInNewTab           = false;

            // Light mode overrides (empty = inherit dark value)
            std::string l_backgroundColor = "";
            std::string l_textColor       = "";

            // Mobile overrides (empty = inherit desktop value)
            std::string m_fontSize      = "";
            std::string m_padding       = "";
            std::string m_borderRadius  = "";
            std::string m_width         = "";
            std::string m_height        = "";

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

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

            // Google Event Trigger
            bool        gEventEnabled  = false;
            std::string gEventName     = "";
            std::string gEventCategory = "";
            std::string gEventLabel    = "";
            std::string gEventValue    = "";
            bool        gConsentUpdate = false;

        public:
            Button() {
                Type = "f77fcdg4-5i98-7h6i-d7f9-2i89g4e989f7";
            }

            virtual ~Button() = default;

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

            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 << "ButtonPlugin 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("Button Settings"));
                settings.appendChild(title);

                // Label
                libhtmlpp::HtmlElement lblLabel("label");
                lblLabel.appendChild(libhtmlpp::TextElement("Button Label: "));
                settings.appendChild(lblLabel);
                libhtmlpp::HtmlElement lblInput("input");
                lblInput.setAttribute("type", "text");
                lblInput.setAttribute("name", "label");
                lblInput.setAttribute("value", label.c_str());
                settings.appendChild(lblInput);

                // Link Type
                libhtmlpp::HtmlElement ltLabel("label");
                ltLabel.appendChild(libhtmlpp::TextElement("Link Type: "));
                settings.appendChild(ltLabel);
                libhtmlpp::HtmlElement ltSelect("select");
                ltSelect.setAttribute("name", "link_type");
                libhtmlpp::HtmlElement ltOptUrl("option");
                ltOptUrl.setAttribute("value", "url");
                if (linkType == "url") ltOptUrl.setAttribute("selected", "selected");
                ltOptUrl.appendChild(libhtmlpp::TextElement("URL"));
                ltSelect.appendChild(ltOptUrl);
                libhtmlpp::HtmlElement ltOptSec("option");
                ltOptSec.setAttribute("value", "section");
                if (linkType == "section") ltOptSec.setAttribute("selected", "selected");
                ltOptSec.appendChild(libhtmlpp::TextElement("Move to Section"));
                ltSelect.appendChild(ltOptSec);
                settings.appendChild(ltSelect);

                // Link URL
                libhtmlpp::HtmlElement urlLabel("label");
                urlLabel.appendChild(libhtmlpp::TextElement("Link URL: "));
                settings.appendChild(urlLabel);
                libhtmlpp::HtmlElement urlInput("input");
                urlInput.setAttribute("type", "text");
                urlInput.setAttribute("name", "link_url");
                urlInput.setAttribute("value", linkUrl.c_str());
                settings.appendChild(urlInput);

                // Section ID (for "Move to Section" mode)
                libhtmlpp::HtmlElement secLabel("label");
                secLabel.appendChild(libhtmlpp::TextElement("Section ID: "));
                settings.appendChild(secLabel);
                libhtmlpp::HtmlElement secInput("input");
                secInput.setAttribute("type", "text");
                secInput.setAttribute("name", "section_id");
                secInput.setAttribute("value", sectionId.c_str());
                settings.appendChild(secInput);

                // Background color
                libhtmlpp::HtmlElement bgLabel("label");
                bgLabel.appendChild(libhtmlpp::TextElement("Background Color: "));
                settings.appendChild(bgLabel);
                libhtmlpp::HtmlElement bgInput("input");
                bgInput.setAttribute("type", "color");
                bgInput.setAttribute("name", "background_color");
                bgInput.setAttribute("value", backgroundColor.c_str());
                settings.appendChild(bgInput);

                // Text color
                libhtmlpp::HtmlElement tcLabel("label");
                tcLabel.appendChild(libhtmlpp::TextElement("Text Color: "));
                settings.appendChild(tcLabel);
                libhtmlpp::HtmlElement tcInput("input");
                tcInput.setAttribute("type", "color");
                tcInput.setAttribute("name", "text_color");
                tcInput.setAttribute("value", textColor.c_str());
                settings.appendChild(tcInput);

                // Font size
                libhtmlpp::HtmlElement fsLabel("label");
                fsLabel.appendChild(libhtmlpp::TextElement("Font Size: "));
                settings.appendChild(fsLabel);
                libhtmlpp::HtmlElement fsInput("input");
                fsInput.setAttribute("type", "text");
                fsInput.setAttribute("name", "font_size");
                fsInput.setAttribute("value", fontSize.c_str());
                settings.appendChild(fsInput);

                // Border radius
                libhtmlpp::HtmlElement brLabel("label");
                brLabel.appendChild(libhtmlpp::TextElement("Border Radius: "));
                settings.appendChild(brLabel);
                libhtmlpp::HtmlElement brInput("input");
                brInput.setAttribute("type", "text");
                brInput.setAttribute("name", "border_radius");
                brInput.setAttribute("value", borderRadius.c_str());
                settings.appendChild(brInput);

                // Open in new tab
                libhtmlpp::HtmlElement ntLabel("label");
                ntLabel.appendChild(libhtmlpp::TextElement("Open in New Tab: "));
                settings.appendChild(ntLabel);
                libhtmlpp::HtmlElement ntInput("input");
                ntInput.setAttribute("type", "checkbox");
                ntInput.setAttribute("name", "open_new_tab");
                if (openInNewTab) ntInput.setAttribute("checked", "checked");
                settings.appendChild(ntInput);

                // Google Event Trigger
                libhtmlpp::HtmlElement geTitle("h4");
                geTitle.appendChild(libhtmlpp::TextElement("Google Event Trigger"));
                settings.appendChild(geTitle);

                libhtmlpp::HtmlElement geLabel("label");
                geLabel.appendChild(libhtmlpp::TextElement("Enable Google Event: "));
                settings.appendChild(geLabel);
                libhtmlpp::HtmlElement geInput("input");
                geInput.setAttribute("type", "checkbox");
                geInput.setAttribute("name", "g_event_enabled");
                if (gEventEnabled) geInput.setAttribute("checked", "checked");
                settings.appendChild(geInput);

                libhtmlpp::HtmlElement genLabel("label");
                genLabel.appendChild(libhtmlpp::TextElement("Event Name: "));
                settings.appendChild(genLabel);
                libhtmlpp::HtmlElement genInput("input");
                genInput.setAttribute("type", "text");
                genInput.setAttribute("name", "g_event_name");
                genInput.setAttribute("value", gEventName.c_str());
                settings.appendChild(genInput);

                libhtmlpp::HtmlElement gecLabel("label");
                gecLabel.appendChild(libhtmlpp::TextElement("Event Category: "));
                settings.appendChild(gecLabel);
                libhtmlpp::HtmlElement gecInput("input");
                gecInput.setAttribute("type", "text");
                gecInput.setAttribute("name", "g_event_category");
                gecInput.setAttribute("value", gEventCategory.c_str());
                settings.appendChild(gecInput);

                libhtmlpp::HtmlElement gelLabel("label");
                gelLabel.appendChild(libhtmlpp::TextElement("Event Label: "));
                settings.appendChild(gelLabel);
                libhtmlpp::HtmlElement gelInput("input");
                gelInput.setAttribute("type", "text");
                gelInput.setAttribute("name", "g_event_label");
                gelInput.setAttribute("value", gEventLabel.c_str());
                settings.appendChild(gelInput);

                libhtmlpp::HtmlElement gevLabel("label");
                gevLabel.appendChild(libhtmlpp::TextElement("Event Value: "));
                settings.appendChild(gevLabel);
                libhtmlpp::HtmlElement gevInput("input");
                gevInput.setAttribute("type", "text");
                gevInput.setAttribute("name", "g_event_value");
                gevInput.setAttribute("value", gEventValue.c_str());
                settings.appendChild(gevInput);

                libhtmlpp::HtmlElement gcuLabel("label");
                gcuLabel.appendChild(libhtmlpp::TextElement("Send Consent Update on Click: "));
                settings.appendChild(gcuLabel);
                libhtmlpp::HtmlElement gcuInput("input");
                gcuInput.setAttribute("type", "checkbox");
                gcuInput.setAttribute("name", "g_consent_update");
                if (gConsentUpdate) gcuInput.setAttribute("checked", "checked");
                settings.appendChild(gcuInput);
            }

            // --- 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("Button"));
                }
            }

            // --- 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;
                };
                auto mkVisible = [](const char *key, const char *value) {
                    json_object *o = json_object_new_object();
                    json_object_object_add(o, "key", json_object_new_string(key));
                    json_object_object_add(o, "value", json_object_new_string(value));
                    return o;
                };
                addField("label", "Button Label", "text");
                addField("link_type", "Link Type", "select", nullptr, "all", mkOpts({"url","section"}));
                addField("link_url", "Link URL", "text", "https://...", "all", nullptr, mkVisible("link_type","url"));
                addField("open_new_tab", "Open in New Tab", "checkbox", nullptr, "all", nullptr, mkVisible("link_type","url"));
                addField("section_id", "Target Section", "section_select", nullptr, "all", nullptr, mkVisible("link_type","section"));
                addField("background_color", "Background Color (Dark)", "color_var");
                addField("l_background_color", "Background Color (Light)", "color_var", nullptr, "light");
                addField("text_color", "Text Color (Dark)", "color_var");
                addField("l_text_color", "Text Color (Light)", "color_var", nullptr, "light");
                addField("font_size", "Font Size", "text", "16px", "desktop");
                addField("border_radius", "Border Radius", "text", "4px", "desktop");
                addField("padding", "Padding", "text", "10px 20px", "desktop");
                addField("width", "Width", "text", "e.g. 200px", "desktop");
                addField("height", "Height", "text", "e.g. auto", "desktop");
                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 { color: red; }");
                addField("g_event_enabled", "Enable Google Event", "checkbox");
                addField("g_event_name", "Event Name", "text", "button_click", "all", nullptr, mkVisible("g_event_enabled", "true"));
                addField("g_event_category", "Event Category", "text", "engagement", "all", nullptr, mkVisible("g_event_enabled", "true"));
                addField("g_event_label", "Event Label", "text", "cta_button", "all", nullptr, mkVisible("g_event_enabled", "true"));
                addField("g_event_value", "Event Value", "text", "1", "all", nullptr, mkVisible("g_event_enabled", "true"));
                addField("g_consent_update", "Send Consent Update on Click", "checkbox", nullptr, "all", nullptr, mkVisible("g_event_enabled", "true"));
                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("label", label.c_str());
                element->SetAttribute("link_url", linkUrl.c_str());
                element->SetAttribute("link_type", linkType.c_str());
                element->SetAttribute("section_id", sectionId.c_str());
                element->SetAttribute("background_color", backgroundColor.c_str());
                element->SetAttribute("text_color", textColor.c_str());
                element->SetAttribute("font_size", fontSize.c_str());
                element->SetAttribute("padding", padding.c_str());
                element->SetAttribute("border_radius", borderRadius.c_str());
                element->SetAttribute("border_style", borderStyle.c_str());
                element->SetAttribute("text_align", textAlign.c_str());
                element->SetAttribute("width", width.c_str());
                element->SetAttribute("height", height.c_str());
                element->SetAttribute("open_new_tab", openInNewTab ? "true" : "false");

                // Light mode overrides
                element->SetAttribute("l_background_color", l_backgroundColor.c_str());
                element->SetAttribute("l_text_color", l_textColor.c_str());

                // Mobile overrides
                element->SetAttribute("m_font_size", m_fontSize.c_str());
                element->SetAttribute("m_padding", m_padding.c_str());
                element->SetAttribute("m_border_radius", m_borderRadius.c_str());
                element->SetAttribute("m_width", m_width.c_str());
                element->SetAttribute("m_height", m_height.c_str());

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

                // Google Event Trigger
                element->SetAttribute("g_event_enabled", gEventEnabled ? "true" : "false");
                element->SetAttribute("g_event_name", gEventName.c_str());
                element->SetAttribute("g_event_category", gEventCategory.c_str());
                element->SetAttribute("g_event_label", gEventLabel.c_str());
                element->SetAttribute("g_event_value", gEventValue.c_str());
                element->SetAttribute("g_consent_update", gConsentUpdate ? "true" : "false");

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

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

                val = xml_data->Attribute("label");            if (val) label = val;
                val = xml_data->Attribute("link_url");         if (val) linkUrl = val;
                val = xml_data->Attribute("link_type");        if (val) linkType = val;
                val = xml_data->Attribute("section_id");       if (val) sectionId = val;
                val = xml_data->Attribute("background_color"); if (val) backgroundColor = val;
                val = xml_data->Attribute("text_color");       if (val) textColor = val;
                val = xml_data->Attribute("font_size");        if (val) fontSize = val;
                val = xml_data->Attribute("padding");          if (val) padding = val;
                val = xml_data->Attribute("border_radius");    if (val) borderRadius = val;
                val = xml_data->Attribute("border_style");     if (val) borderStyle = val;
                val = xml_data->Attribute("text_align");       if (val) textAlign = val;
                val = xml_data->Attribute("width");            if (val) width = val;
                val = xml_data->Attribute("height");           if (val) height = val;
                val = xml_data->Attribute("open_new_tab");
                if (val) openInNewTab = (strcmp(val, "true") == 0);

                // Light mode overrides
                val = xml_data->Attribute("l_background_color");
                if (val) l_backgroundColor = val;
                val = xml_data->Attribute("l_text_color");
                if (val) l_textColor = val;

                // Mobile overrides
                val = xml_data->Attribute("m_font_size");
                if (val) m_fontSize = val;
                val = xml_data->Attribute("m_padding");
                if (val) m_padding = val;
                val = xml_data->Attribute("m_border_radius");
                if (val) m_borderRadius = val;
                val = xml_data->Attribute("m_width");
                if (val) m_width = val;
                val = xml_data->Attribute("m_height");
                if (val) m_height = val;

                // Visibility
                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);

                // Google Event Trigger
                val = xml_data->Attribute("g_event_enabled");
                if (val) gEventEnabled = (strcmp(val, "true") == 0);
                val = xml_data->Attribute("g_event_name");
                if (val) gEventName = val;
                val = xml_data->Attribute("g_event_category");
                if (val) gEventCategory = val;
                val = xml_data->Attribute("g_event_label");
                if (val) gEventLabel = val;
                val = xml_data->Attribute("g_event_value");
                if (val) gEventValue = val;
                val = xml_data->Attribute("g_consent_update");
                if (val) gConsentUpdate = (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, "label", json_object_new_string(label.c_str()));
                json_object_object_add(obj, "link_url", json_object_new_string(linkUrl.c_str()));
                json_object_object_add(obj, "link_type", json_object_new_string(linkType.c_str()));
                json_object_object_add(obj, "section_id", json_object_new_string(sectionId.c_str()));
                json_object_object_add(obj, "background_color", json_object_new_string(backgroundColor.c_str()));
                json_object_object_add(obj, "text_color", json_object_new_string(textColor.c_str()));
                json_object_object_add(obj, "font_size", json_object_new_string(fontSize.c_str()));
                json_object_object_add(obj, "padding", json_object_new_string(padding.c_str()));
                json_object_object_add(obj, "border_radius", json_object_new_string(borderRadius.c_str()));
                json_object_object_add(obj, "border_style", json_object_new_string(borderStyle.c_str()));
                json_object_object_add(obj, "text_align", json_object_new_string(textAlign.c_str()));
                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, "open_new_tab", json_object_new_boolean(openInNewTab));
                json_object_object_add(obj, "l_background_color", json_object_new_string(l_backgroundColor.c_str()));
                json_object_object_add(obj, "l_text_color", json_object_new_string(l_textColor.c_str()));
                json_object_object_add(obj, "m_font_size", json_object_new_string(m_fontSize.c_str()));
                json_object_object_add(obj, "m_padding", json_object_new_string(m_padding.c_str()));
                json_object_object_add(obj, "m_border_radius", json_object_new_string(m_borderRadius.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()));
                json_object_object_add(obj, "g_event_enabled", json_object_new_boolean(gEventEnabled));
                json_object_object_add(obj, "g_event_name", json_object_new_string(gEventName.c_str()));
                json_object_object_add(obj, "g_event_category", json_object_new_string(gEventCategory.c_str()));
                json_object_object_add(obj, "g_event_label", json_object_new_string(gEventLabel.c_str()));
                json_object_object_add(obj, "g_event_value", json_object_new_string(gEventValue.c_str()));
                json_object_object_add(obj, "g_consent_update", json_object_new_boolean(gConsentUpdate));
                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, "label", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) label = val;
                }
                if (json_object_object_get_ex(obj, "link_url", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) linkUrl = val;
                }
                if (json_object_object_get_ex(obj, "link_type", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) linkType = val;
                }
                if (json_object_object_get_ex(obj, "section_id", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) sectionId = val;
                }
                if (json_object_object_get_ex(obj, "background_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) backgroundColor = val;
                }
                if (json_object_object_get_ex(obj, "text_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) textColor = val;
                }
                if (json_object_object_get_ex(obj, "font_size", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) fontSize = val;
                }
                if (json_object_object_get_ex(obj, "padding", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) padding = 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, "border_style", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) borderStyle = val;
                }
                if (json_object_object_get_ex(obj, "text_align", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) textAlign = val;
                }
                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, "open_new_tab", &val_obj)) {
                    if (json_object_is_type(val_obj, json_type_string)) {
                        const char *s = json_object_get_string(val_obj);
                        openInNewTab = (s && (strcmp(s, "true") == 0 || strcmp(s, "1") == 0));
                    } else {
                        openInNewTab = 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, "l_text_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) l_textColor = val;
                }
                if (json_object_object_get_ex(obj, "m_font_size", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_fontSize = val;
                }
                if (json_object_object_get_ex(obj, "m_padding", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_padding = val;
                }
                if (json_object_object_get_ex(obj, "m_border_radius", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_borderRadius = 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)) {
                    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;
                }
                if (json_object_object_get_ex(obj, "g_event_enabled", &val_obj)) {
                    if (json_object_is_type(val_obj, json_type_string)) {
                        const char *s = json_object_get_string(val_obj);
                        gEventEnabled = (s && (strcmp(s, "true") == 0 || strcmp(s, "1") == 0));
                    } else {
                        gEventEnabled = json_object_get_boolean(val_obj);
                    }
                }
                if (json_object_object_get_ex(obj, "g_event_name", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) gEventName = val;
                }
                if (json_object_object_get_ex(obj, "g_event_category", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) gEventCategory = val;
                }
                if (json_object_object_get_ex(obj, "g_event_label", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) gEventLabel = val;
                }
                if (json_object_object_get_ex(obj, "g_event_value", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) gEventValue = val;
                }
                if (json_object_object_get_ex(obj, "g_consent_update", &val_obj)) {
                    if (json_object_is_type(val_obj, json_type_string)) {
                        const char *s = json_object_get_string(val_obj);
                        gConsentUpdate = (s && (strcmp(s, "true") == 0 || strcmp(s, "1") == 0));
                    } else {
                        gConsentUpdate = json_object_get_boolean(val_obj);
                    }
                }

                json_object_put(obj);
            }

            // --- HTML Rendering ---
            void Render(libhtmlpp::HtmlElement &el) const override {
                libhtmlpp::HtmlElement wrapper("div");

                // Unique CSS class for responsive targeting
                std::string responsiveClass = std::string("we-") + getInstanceId().c_str();

                if (!textAlign.empty()) {
                    wrapper.setAttribute("style", ("text-align: " + textAlign + ";").c_str());
                }

                libhtmlpp::HtmlElement a("a");
                // Build Google event trigger JS snippet
                // Build Google consent update + event trigger JS snippet
                std::string gtagSnippet;
                if (gEventEnabled && !gEventName.empty()) {
                    gtagSnippet = "if(typeof gtag==='function'){";
                    if (gConsentUpdate) {
                        gtagSnippet += "gtag('consent','update',{"
                                       "'ad_storage':'granted',"
                                       "'ad_user_data':'granted',"
                                       "'ad_personalization':'granted',"
                                       "'analytics_storage':'granted',"
                                       "'functionality_storage':'granted',"
                                       "'personalization_storage':'granted',"
                                       "'security_storage':'granted'});";
                    }
                    gtagSnippet += "gtag('event','";
                    gtagSnippet += gEventName;
                    gtagSnippet += "',{";
                    if (!gEventCategory.empty())
                        gtagSnippet += "'event_category':'" + gEventCategory + "',";
                    if (!gEventLabel.empty())
                        gtagSnippet += "'event_label':'" + gEventLabel + "',";
                    if (!gEventValue.empty())
                        gtagSnippet += "'value':'" + gEventValue + "',";
                    gtagSnippet += "});}";
                } else if (gEventEnabled && gConsentUpdate) {
                    // Consent update only, no event name
                    gtagSnippet = "if(typeof gtag==='function'){gtag('consent','update',{"
                                  "'ad_storage':'granted',"
                                  "'ad_user_data':'granted',"
                                  "'ad_personalization':'granted',"
                                  "'analytics_storage':'granted',"
                                  "'functionality_storage':'granted',"
                                  "'personalization_storage':'granted',"
                                  "'security_storage':'granted'});}";
                }

                if (linkType == "section" && !sectionId.empty()) {
                    std::string anchor = "#we-" + sectionId;
                    a.setAttribute("href", anchor.c_str());
                    std::string onclick = "event.preventDefault();";
                    if (!gtagSnippet.empty()) onclick += gtagSnippet;
                    onclick += "document.getElementById(this.getAttribute('href').substring(1))"
                               ".scrollIntoView({behavior:'smooth'});";
                    a.setAttribute("onclick", onclick.c_str());
                } else {
                    a.setAttribute("href", linkUrl.c_str());
                    if (openInNewTab) {
                        a.setAttribute("target", "_blank");
                        a.setAttribute("rel", "noopener noreferrer");
                    }
                    if (!gtagSnippet.empty()) {
                        a.setAttribute("onclick", gtagSnippet.c_str());
                    }
                }

                std::string style;
                style += "display: inline-block;";
                style += " background-color: " + backgroundColor + ";";
                style += " color: " + textColor + ";";
                style += " font-size: " + fontSize + ";";
                style += " padding: " + padding + ";";
                style += " border-radius: " + borderRadius + ";";
                style += " border: " + borderStyle + ";";
                style += " text-decoration: none; cursor: pointer;";
                if (!width.empty()) style += " width: " + width + ";";
                if (!height.empty()) style += " height: " + height + ";";
                if (hidden) style += " display: none;";

                a.setAttribute("style", style.c_str());
                a.setAttribute("class", responsiveClass.c_str());
                a.appendChild(libhtmlpp::TextElement(label.c_str()));

                // Mobile responsive overrides via media query
                std::string mobileCSS;
                if (!m_fontSize.empty()) mobileCSS += " font-size: " + m_fontSize + " !important;";
                if (!m_padding.empty()) mobileCSS += " padding: " + m_padding + " !important;";
                if (!m_borderRadius.empty()) mobileCSS += " border-radius: " + m_borderRadius + " !important;";
                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: inline-block !important;";

                // Light mode overrides via prefers-color-scheme
                std::string lightCSS;
                if (!l_backgroundColor.empty()) lightCSS += " background-color: " + l_backgroundColor + " !important;";
                if (!l_textColor.empty()) lightCSS += " color: " + l_textColor + " !important;";

                std::string allMediaCSS;
                if (!mobileCSS.empty()) {
                    allMediaCSS += "@media (max-width: 768px) { ." + responsiveClass + " {" + mobileCSS + " } } ";
                }
                if (!lightCSS.empty()) {
                    allMediaCSS += "@media (prefers-color-scheme: light) { ." + responsiveClass + " {" + lightCSS + " } }";
                }

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

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

                wrapper.appendChild(a);
                el.appendChild(wrapper);
            }

            // --- Element Management (Button is a leaf) ---
            bool canHaveChildren() const override { return false; }
            bool haveChilds() override { return false; }

            void setChildElement(const EditPlugin &) override {
                logError("Button cannot have children.");
            }
            void unsetChildElement(const EditPlugin &) override {
                logError("Button cannot have children.");
            }
            void addNextElement(const EditPlugin &) override {
                logError("Button does not support addNextElement.");
            }
            void remNextElement(const EditPlugin &) override {
                logError("Button does not support remNextElement.");
            }
        };
    };
}

// --- Factory Functions ---
EXPORT blogi::webedit::Button* create() {
    return new blogi::webedit::Button();
}

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