/*******************************************************************************
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 <vector>

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

#include "plugin.h"

extern "C" {
    namespace blogi::webedit {

        class Table : public EditPlugin {
        private:
            int numRows    = 2;
            int numCols    = 2;
            bool hasHeader = true;
            std::string borderStyle   = "1px solid var(--blogi-border, #ccc)";
            std::string backgroundColor = "";
            std::string cellPadding   = "8px";
            std::string width         = "";
            std::string height        = "";

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

            // Mobile overrides (empty = inherit desktop value)
            std::string m_cellPadding = "";
            std::string m_borderStyle = "";
            std::string m_width       = "";
            std::string m_height      = "";

            // Vertical alignment
            std::string verticalAlign = "";
            std::string m_verticalAlign = "";

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

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

            // Cell data stored as row-major flat vector
            std::vector<std::string> cellData;

            void ensureCellData() {
                size_t needed = static_cast<size_t>(numRows * numCols);
                if (cellData.size() < needed) {
                    cellData.resize(needed, "");
                }
            }

            std::string& getCellRef(int row, int col) {
                ensureCellData();
                return cellData[static_cast<size_t>(row * numCols + col)];
            }

            const std::string& getCellVal(int row, int col) const {
                static const std::string empty;
                size_t idx = static_cast<size_t>(row * numCols + col);
                if (idx < cellData.size()) return cellData[idx];
                return empty;
            }

        public:
            Table() {
                Type = "d55dabe2-3g76-5f4g-b5d7-0g67e2c767d5";
                ensureCellData();
            }

            virtual ~Table() = default;

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

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

                // Rows
                libhtmlpp::HtmlElement rowLabel("label");
                rowLabel.appendChild(libhtmlpp::TextElement("Rows: "));
                settings.appendChild(rowLabel);
                libhtmlpp::HtmlElement rowInput("input");
                rowInput.setAttribute("type", "number");
                rowInput.setAttribute("name", "num_rows");
                rowInput.setAttribute("value", std::to_string(numRows).c_str());
                rowInput.setAttribute("min", "1");
                settings.appendChild(rowInput);

                // Columns
                libhtmlpp::HtmlElement colLabel("label");
                colLabel.appendChild(libhtmlpp::TextElement("Columns: "));
                settings.appendChild(colLabel);
                libhtmlpp::HtmlElement colInput("input");
                colInput.setAttribute("type", "number");
                colInput.setAttribute("name", "num_cols");
                colInput.setAttribute("value", std::to_string(numCols).c_str());
                colInput.setAttribute("min", "1");
                settings.appendChild(colInput);

                // Header
                libhtmlpp::HtmlElement hdrLabel("label");
                hdrLabel.appendChild(libhtmlpp::TextElement("Has Header Row: "));
                settings.appendChild(hdrLabel);
                libhtmlpp::HtmlElement hdrInput("input");
                hdrInput.setAttribute("type", "checkbox");
                hdrInput.setAttribute("name", "has_header");
                if (hasHeader) hdrInput.setAttribute("checked", "checked");
                settings.appendChild(hdrInput);

                // Border style
                libhtmlpp::HtmlElement bsLabel("label");
                bsLabel.appendChild(libhtmlpp::TextElement("Border Style: "));
                settings.appendChild(bsLabel);
                libhtmlpp::HtmlElement bsInput("input");
                bsInput.setAttribute("type", "text");
                bsInput.setAttribute("name", "border_style");
                bsInput.setAttribute("value", borderStyle.c_str());
                settings.appendChild(bsInput);

                // Cell padding
                libhtmlpp::HtmlElement cpLabel("label");
                cpLabel.appendChild(libhtmlpp::TextElement("Cell Padding: "));
                settings.appendChild(cpLabel);
                libhtmlpp::HtmlElement cpInput("input");
                cpInput.setAttribute("type", "text");
                cpInput.setAttribute("name", "cell_padding");
                cpInput.setAttribute("value", cellPadding.c_str());
                settings.appendChild(cpInput);
            }

            // --- 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, "set_cell") == 0) {
                    json_object *row_obj = nullptr, *col_obj = nullptr, *val_obj = nullptr;
                    if (json_object_object_get_ex(request, "row", &row_obj) &&
                        json_object_object_get_ex(request, "col", &col_obj) &&
                        json_object_object_get_ex(request, "value", &val_obj)) {
                        int r = json_object_get_int(row_obj);
                        int c = json_object_get_int(col_obj);
                        const char *v = json_object_get_string(val_obj);
                        if (r >= 0 && r < numRows && c >= 0 && c < numCols && v) {
                            getCellRef(r, c) = v;
                        }
                    }
                } 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()));

                    // Also serialize cell data
                    json_object *cells_array = json_object_new_array();
                    ensureCellData();
                    for (int r = 0; r < numRows; ++r) {
                        json_object *row_array = json_object_new_array();
                        for (int c = 0; c < numCols; ++c) {
                            json_object_array_add(row_array, json_object_new_string(getCellVal(r, c).c_str()));
                        }
                        json_object_array_add(cells_array, row_array);
                    }
                    json_object_object_add(result_obj, "cells", cells_array);

                    if (response) {
                        json_object_object_add(response, "data", result_obj);
                    }
                } else if (strcmp(action, "add_row") == 0) {
                    numRows++;
                    ensureCellData();
                } else if (strcmp(action, "add_col") == 0) {
                    // Need to rearrange cell data
                    std::vector<std::string> newData(static_cast<size_t>(numRows * (numCols + 1)), "");
                    for (int r = 0; r < numRows; ++r) {
                        for (int c = 0; c < numCols; ++c) {
                            newData[static_cast<size_t>(r * (numCols + 1) + c)] = getCellVal(r, c);
                        }
                    }
                    numCols++;
                    cellData = std::move(newData);
                } 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("Table"));
                }
            }

            // --- 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, int min = -1) {
                    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 (min >= 0) json_object_object_add(f, "min", json_object_new_int(min));
                    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("num_rows", "Rows", "number", nullptr, "all", nullptr, 1);
                addField("num_cols", "Columns", "number", nullptr, "all", nullptr, 1);
                addField("has_header", "Has Header Row", "checkbox");
                addField("border_style", "Border Style", "text", "1px solid #ccc", "desktop");
                addField("cell_padding", "Cell Padding", "text", "8px", "desktop");
                addField("width", "Width", "text", "e.g. 100%", "desktop");
                addField("height", "Height", "text", "e.g. auto", "desktop");
                addField("background_color", "Background Color (Dark)", "color_var");
                addField("l_background_color", "Background Color (Light)", "color_var", nullptr, "light");
                addField("vertical_align", "Vertical Align", "select", nullptr, "desktop", mkOpts({"top","center","bottom"}));
                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; }");
                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("num_rows", numRows);
                element->SetAttribute("num_cols", numCols);
                element->SetAttribute("has_header", hasHeader ? "true" : "false");
                element->SetAttribute("border_style", borderStyle.c_str());
                element->SetAttribute("background_color", backgroundColor.c_str());
                element->SetAttribute("cell_padding", cellPadding.c_str());
                element->SetAttribute("width", width.c_str());
                element->SetAttribute("height", height.c_str());

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

                // Mobile overrides
                element->SetAttribute("m_cell_padding", m_cellPadding.c_str());
                element->SetAttribute("m_border_style", m_borderStyle.c_str());
                element->SetAttribute("m_width", m_width.c_str());
                element->SetAttribute("m_height", m_height.c_str());

                // Vertical alignment
                element->SetAttribute("vertical_align", verticalAlign.c_str());
                element->SetAttribute("m_vertical_align", m_verticalAlign.c_str());

                // Visibility
                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 cell data as nested XML
                ensureCellData();
                for (int r = 0; r < numRows; ++r) {
                    tinyxml2::XMLElement* rowEl = doc->NewElement("Row");
                    rowEl->SetAttribute("index", r);
                    for (int c = 0; c < numCols; ++c) {
                        tinyxml2::XMLElement* cellEl = doc->NewElement("Cell");
                        cellEl->SetAttribute("col", c);
                        cellEl->SetText(getCellVal(r, c).c_str());
                        rowEl->InsertEndChild(cellEl);
                    }
                    element->InsertEndChild(rowEl);
                }

                // Serialize child widgets
                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;

                xml_data->QueryIntAttribute("num_rows", &numRows);
                xml_data->QueryIntAttribute("num_cols", &numCols);

                val = xml_data->Attribute("has_header");
                if (val) hasHeader = (strcmp(val, "true") == 0);

                val = xml_data->Attribute("border_style");
                if (val) borderStyle = val;

                val = xml_data->Attribute("background_color");
                if (val) backgroundColor = val;

                val = xml_data->Attribute("cell_padding");
                if (val) cellPadding = val;

                val = xml_data->Attribute("width");
                if (val) width = val;
                val = xml_data->Attribute("height");
                if (val) height = val;

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

                // Mobile overrides
                val = xml_data->Attribute("m_cell_padding");
                if (val) m_cellPadding = val;
                val = xml_data->Attribute("m_border_style");
                if (val) m_borderStyle = val;
                val = xml_data->Attribute("m_width");
                if (val) m_width = val;
                val = xml_data->Attribute("m_height");
                if (val) m_height = val;

                // Vertical alignment
                val = xml_data->Attribute("vertical_align");
                if (val) verticalAlign = val;
                val = xml_data->Attribute("m_vertical_align");
                if (val) m_verticalAlign = 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);

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

                ensureCellData();

                // Load cell data from XML
                tinyxml2::XMLElement* rowEl = xml_data->FirstChildElement("Row");
                while (rowEl) {
                    int r = 0;
                    rowEl->QueryIntAttribute("index", &r);
                    tinyxml2::XMLElement* cellEl = rowEl->FirstChildElement("Cell");
                    while (cellEl) {
                        int c = 0;
                        cellEl->QueryIntAttribute("col", &c);
                        const char* text = cellEl->GetText();
                        if (text && r >= 0 && r < numRows && c >= 0 && c < numCols) {
                            getCellRef(r, c) = text;
                        }
                        cellEl = cellEl->NextSiblingElement("Cell");
                    }
                    rowEl = rowEl->NextSiblingElement("Row");
                }
            }

            // --- JSON Properties Helpers ---
            std::string toPropertiesJson() {
                json_object *obj = json_object_new_object();
                json_object_object_add(obj, "num_rows", json_object_new_int(numRows));
                json_object_object_add(obj, "num_cols", json_object_new_int(numCols));
                json_object_object_add(obj, "has_header", json_object_new_boolean(hasHeader));
                json_object_object_add(obj, "border_style", json_object_new_string(borderStyle.c_str()));
                json_object_object_add(obj, "background_color", json_object_new_string(backgroundColor.c_str()));
                json_object_object_add(obj, "cell_padding", json_object_new_string(cellPadding.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, "l_background_color", json_object_new_string(l_backgroundColor.c_str()));
                json_object_object_add(obj, "m_cell_padding", json_object_new_string(m_cellPadding.c_str()));
                json_object_object_add(obj, "m_border_style", json_object_new_string(m_borderStyle.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, "vertical_align", json_object_new_string(verticalAlign.c_str()));
                json_object_object_add(obj, "m_vertical_align", json_object_new_string(m_verticalAlign.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, "num_rows", &val_obj)) {
                    int v = json_object_get_int(val_obj); if (v > 0) numRows = v;
                }
                if (json_object_object_get_ex(obj, "num_cols", &val_obj)) {
                    int v = json_object_get_int(val_obj); if (v > 0) numCols = v;
                }
                if (json_object_object_get_ex(obj, "has_header", &val_obj)) {
                    hasHeader = json_object_get_boolean(val_obj);
                }
                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, "background_color", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) backgroundColor = val;
                }
                if (json_object_object_get_ex(obj, "cell_padding", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val && val[0]) cellPadding = 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, "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_cell_padding", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_cellPadding = val;
                }
                if (json_object_object_get_ex(obj, "m_border_style", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_borderStyle = 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, "vertical_align", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) verticalAlign = val;
                }
                if (json_object_object_get_ex(obj, "m_vertical_align", &val_obj)) {
                    val = json_object_get_string(val_obj); if (val) m_verticalAlign = 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);
                ensureCellData();
            }

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

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

                std::string tableStyle = "border-collapse: collapse;";
                if (!width.empty()) {
                    tableStyle += " width: " + width + ";";
                } else {
                    tableStyle += " width: 100%;";
                }
                if (!height.empty()) {
                    tableStyle += " height: " + height + ";";
                }
                if (!backgroundColor.empty()) {
                    tableStyle += " background-color: " + backgroundColor + ";";
                }
                if (hidden) {
                    tableStyle += " display: none;";
                }
                table.setAttribute("style", tableStyle.c_str());
                table.setAttribute("class", responsiveClass.c_str());

                // Mobile responsive overrides via media query
                std::string mobileCSS;
                // Table-level responsive handled via th/td class below

                std::string cellResponsiveClass = responsiveClass + "-cell";
                std::string mobileCellCSS;
                if (!m_cellPadding.empty()) mobileCellCSS += " padding: " + m_cellPadding + " !important;";
                if (!m_borderStyle.empty()) mobileCellCSS += " border: " + m_borderStyle + " !important;";
                if (!m_verticalAlign.empty()) {
                    if (m_verticalAlign == "center") mobileCellCSS += " vertical-align: middle !important;";
                    else mobileCellCSS += " vertical-align: " + m_verticalAlign + " !important;";
                }

                // Table-level mobile hidden
                std::string mobileTableCSS;
                if (!m_width.empty()) mobileTableCSS += " width: " + m_width + " !important;";
                if (!m_height.empty()) mobileTableCSS += " height: " + m_height + " !important;";
                if (m_hidden) mobileTableCSS += " display: none !important;";
                else if (hidden) mobileTableCSS += " display: table !important;";

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

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

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

                // Build a flat list of child widgets for distribution into cells
                const EditPlugin *curChild = child;

                int startRow = 0;

                // Header row
                if (hasHeader && numRows > 0) {
                    libhtmlpp::HtmlElement thead("thead");
                    libhtmlpp::HtmlElement tr("tr");
                    for (int c = 0; c < numCols; ++c) {
                        libhtmlpp::HtmlElement th("th");
                        std::string thStyle = "border: " + borderStyle + "; padding: " + cellPadding + "; font-weight: bold;";
                        if (!verticalAlign.empty()) {
                            if (verticalAlign == "center") thStyle += " vertical-align: middle;";
                            else thStyle += " vertical-align: " + verticalAlign + ";";
                        }
                        th.setAttribute("style", thStyle.c_str());
                        if (!mobileCellCSS.empty()) th.setAttribute("class", cellResponsiveClass.c_str());
                        // Render child widget into header cell if available
                        if (curChild) {
                            curChild->Render(th);
                            curChild = curChild->nextElement();
                        } else {
                            th.appendChild(libhtmlpp::TextElement(getCellVal(0, c).c_str()));
                        }
                        tr.appendChild(th);
                    }
                    thead.appendChild(tr);
                    table.appendChild(thead);
                    startRow = 1;
                }

                // Body rows
                libhtmlpp::HtmlElement tbody("tbody");
                for (int r = startRow; r < numRows; ++r) {
                    libhtmlpp::HtmlElement tr("tr");
                    for (int c = 0; c < numCols; ++c) {
                        libhtmlpp::HtmlElement td("td");
                        std::string tdStyle = "border: " + borderStyle + "; padding: " + cellPadding + ";";
                        if (!verticalAlign.empty()) {
                            if (verticalAlign == "center") tdStyle += " vertical-align: middle;";
                            else tdStyle += " vertical-align: " + verticalAlign + ";";
                        }
                        td.setAttribute("style", tdStyle.c_str());
                        if (!mobileCellCSS.empty()) td.setAttribute("class", cellResponsiveClass.c_str());
                        // Render child widget into cell if available
                        if (curChild) {
                            curChild->Render(td);
                            curChild = curChild->nextElement();
                        } else {
                            td.appendChild(libhtmlpp::TextElement(getCellVal(r, c).c_str()));
                        }
                        tr.appendChild(td);
                    }
                    tbody.appendChild(tr);
                }
                table.appendChild(tbody);

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

                el.appendChild(table);
            }

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

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