/*******************************************************************************
 * Copyright (c) 2023, 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 <iostream>
#include <algorithm>

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

#include <conf.h>

#include "static.h"
#include "conf.h"
#include <i18n.h>
#include <compress.h>

blogi::StaticPage::StaticPage(){
}

blogi::StaticPage::~StaticPage(){
}

void blogi::StaticPage::initPlugin(){
    registerGPO("12434b46-9adf-43ec-a88e-8fd4c2cfeec7",
                "Blogi: Static Page Settings",
                "Controls access to static page management",
                true);

    dbpp::SQL sql;
    dbpp::DBResult res;
    sql << "CREATE TABLE IF NOT EXISTS static_content ("
        << "id " << Args->database[0]->getUUIDType(sql) << " PRIMARY KEY,"
        << "text text,"
        << "meta text,"
        << "url character varying(255) NOT NULL UNIQUE"
        << ");";
    Args->database[0]->exec(sql,res);
}

bool blogi::StaticPage::haveSettings(){
    return true;
}

void blogi::StaticPage::GetSiteMap(const int tid,std::map<libhttppp::HttpUrl,std::chrono::year_month_day> &sitemap){
    dbpp::SQL sql;
    dbpp::DBResult res;
    sql << "select url from static_content";
    int result = Args->database[tid]->exec(sql,res);
    auto zero_date = std::chrono::year(1970) / std::chrono::month(1) / std::chrono::day(1);
    for(int i=0; i<result; ++i){
        sitemap.emplace(Args->config->getsiteurl()+"/staticpage/"+res[i][0],zero_date);
    }
}

json_object* blogi::StaticPage::Settings(const int tid,libhttppp::HttpRequest &req,const std::string &sessionid){
    std::string surl,curl=req.getRequestURL();
    std::string _lang = getLang(*Args->database[tid]);
    size_t urlen = curl.length();
    size_t prelen = Args->config->buildurl("settings/staticpage/").length();
    if(prelen < urlen){
        size_t send=0,sublen=urlen-prelen;

        for(size_t i =0; i<=sublen; ++i){
            switch(curl[prelen+i]){
                case '\0':
                    send=i;
                    goto SENDFIND;
                case '/':
                    send=i;
                    goto SENDFIND;
                default:
                    continue;
            }
        }
        goto SETTINGSINDEX;
SENDFIND:
        surl=curl.substr(prelen,send);

        if(surl=="newpage"){
            return newPage(tid,req);
        }else if(surl=="delpage"){
            return delPage(tid,req);
        }else if(surl=="editpage"){
            return editPage(tid,req);
        }

    }
SETTINGSINDEX:
    dbpp::SQL sql;
    dbpp::DBResult res;

    sql << "select id,url from static_content";

    int scount=Args->database[tid]->exec(sql,res);

    json_object *jroot = json_object_new_object();
    json_object_object_add(jroot, "title", json_object_new_string(tr(_lang,"Static Pages").c_str()));

    json_object *jtable = json_object_new_object();
    json_object *jheaders = json_object_new_array();
    json_object_array_add(jheaders, json_object_new_string("ID"));
    json_object_array_add(jheaders, json_object_new_string("URL"));
    json_object_array_add(jheaders, json_object_new_string(tr(_lang,"Options").c_str()));
    json_object_object_add(jtable, "headers", jheaders);

    json_object *jrows = json_object_new_array();
    for(int i=0; i<scount; ++i){
        json_object *jrow = json_object_new_object();
        json_object *jcells = json_object_new_array();
        json_object_array_add(jcells, json_object_new_string(res[i][0]));
        std::string fullUrl = Args->config->buildurl("staticpage/") + std::string(res[i][1]);
        json_object_array_add(jcells, json_object_new_string(fullUrl.c_str()));
        json_object_object_add(jrow, "cells", jcells);

        json_object *jactions = json_object_new_array();
        json_object *jedit = json_object_new_object();
        json_object_object_add(jedit, "label", json_object_new_string(tr(_lang,"Edit").c_str()));
        std::string editUrl = Args->config->buildurl("settings/staticpage/editpage?pageid=") + std::string(res[i][0]);
        json_object_object_add(jedit, "url", json_object_new_string(editUrl.c_str()));
        json_object_array_add(jactions, jedit);

        json_object *jremove = json_object_new_object();
        json_object_object_add(jremove, "label", json_object_new_string(tr(_lang,"Remove").c_str()));
        std::string removeUrl = Args->config->buildurl("settings/staticpage/delpage?pageid=") + std::string(res[i][0]);
        json_object_object_add(jremove, "url", json_object_new_string(removeUrl.c_str()));
        json_object_array_add(jactions, jremove);

        json_object_object_add(jrow, "actions", jactions);
        json_object_array_add(jrows, jrow);
    }
    json_object_object_add(jtable, "rows", jrows);
    json_object_object_add(jroot, "table", jtable);

    json_object *jlinks = json_object_new_array();
    json_object *jnewLink = json_object_new_object();
    json_object_object_add(jnewLink, "label", json_object_new_string(tr(_lang,"Create New Page").c_str()));
    json_object_object_add(jnewLink, "url", json_object_new_string(Args->config->buildurl("settings/staticpage/newpage").c_str()));
    json_object_array_add(jlinks, jnewLink);
    json_object_object_add(jroot, "links", jlinks);

    return jroot;
}

json_object* blogi::StaticPage::newPage(const int tid,libhttppp::HttpRequest& req){
    std::string _lang = getLang(*Args->database[tid]);
    char url[512];
    dbpp::SQL sql;
    dbpp::DBResult res;
    std::vector<char> sbuf;

    libhttppp::HttpForm form;
    form.parse(req);
    std::string surl,meta,err;
    libhtmlpp::HtmlString text;

    try {

        for (const auto &curformdat : form.multipartData()) {
            for(const auto &curctdisp : curformdat.dispositions){
                if(curctdisp.key != "name"){
                    continue;
                }

                std::string data;

                std::copy(curformdat.value.begin(),curformdat.value.end(),
                      std::inserter<std::string>(data,data.begin()));

                if(curctdisp.value=="url"){
                    libhtmlpp::HtmlEncode(data.c_str(),surl);
                }else if(curctdisp.value=="meta"){
                    libhtmlpp::HtmlEncode(data.c_str(),meta);
                }else if(curctdisp.value=="text"){
                    text=data;
                }
            }
        }
    }catch(...){};

    if(!surl.empty() && ( !text.empty()) ){
        try{
            text.parse();
        }catch(libhtmlpp::HTMLException &e){
            std::cerr << e.what() << std::endl;
            return nullptr;
        }
        uuid::uuid id;
        id.generate();

        sql << "INSERT INTO static_content (id,url,meta,text) VALUES('"
            << id.c_str() << "\',\'"
            << sql.escaped(sbuf,surl.c_str()) << "\',\'"
            << sql.escaped(sbuf,meta.c_str()) << "\',\'"
            << sql.escaped(sbuf,text.c_str()) <<"\');";
        try{
           Args->database[tid]->exec(sql,res);
        }catch(const char *e){
            std::cerr << e << std::endl;
            return nullptr;
        }
        sql.clear();

        json_object *jroot = json_object_new_object();
        json_object_object_add(jroot, "title", json_object_new_string(tr(_lang,"Static Pages").c_str()));
        json_object_object_add(jroot, "message", json_object_new_string(tr(_lang,"Added successfully!").c_str()));
        json_object_object_add(jroot, "message_type", json_object_new_string("success"));

        json_object *jnav = json_object_new_array();
        json_object *jback = json_object_new_object();
        json_object_object_add(jback, "label", json_object_new_string(tr(_lang,"Back to list").c_str()));
        json_object_object_add(jback, "url", json_object_new_string(Args->config->buildurl("settings/staticpage").c_str()));
        json_object_array_add(jnav, jback);
        json_object_object_add(jroot, "nav", jnav);

        return jroot;
    }

    json_object *jroot = json_object_new_object();
    json_object_object_add(jroot, "title", json_object_new_string(tr(_lang,"New Page").c_str()));

    if(!err.empty()){
        json_object_object_add(jroot, "message", json_object_new_string((tr(_lang,"Invalid HTML:") + " " + err).c_str()));
        json_object_object_add(jroot, "message_type", json_object_new_string("error"));
    }

    // Form
    json_object *jform = json_object_new_object();
    json_object_object_add(jform, "method", json_object_new_string("POST"));
    json_object_object_add(jform, "action", json_object_new_string(Args->config->buildurl("settings/staticpage/newpage").c_str()));
    json_object_object_add(jform, "enctype", json_object_new_string("multipart/form-data"));

    json_object *jfields = json_object_new_array();

    json_object *jfUrl = json_object_new_object();
    json_object_object_add(jfUrl, "name", json_object_new_string("url"));
    json_object_object_add(jfUrl, "label", json_object_new_string(tr(_lang,"Url").c_str()));
    json_object_object_add(jfUrl, "type", json_object_new_string("text"));
    json_object_object_add(jfUrl, "value", json_object_new_string(surl.empty() ? "newurl" : surl.c_str()));
    json_object_array_add(jfields, jfUrl);

    json_object *jfMeta = json_object_new_object();
    json_object_object_add(jfMeta, "name", json_object_new_string("meta"));
    json_object_object_add(jfMeta, "label", json_object_new_string(tr(_lang,"Meta").c_str()));
    json_object_object_add(jfMeta, "type", json_object_new_string("textarea"));
    json_object_object_add(jfMeta, "value", json_object_new_string(tr(_lang,"Keywords for Google").c_str()));
    json_object_array_add(jfields, jfMeta);

    json_object *jfText = json_object_new_object();
    json_object_object_add(jfText, "name", json_object_new_string("text"));
    json_object_object_add(jfText, "label", json_object_new_string(tr(_lang,"Content").c_str()));
    json_object_object_add(jfText, "type", json_object_new_string("textarea"));
    json_object_object_add(jfText, "value", json_object_new_string(""));
    json_object_array_add(jfields, jfText);

    json_object_object_add(jform, "fields", jfields);
    json_object_object_add(jform, "submit", json_object_new_string(tr(_lang,"Save").c_str()));
    json_object_object_add(jroot, "form", jform);

    // Back link
    json_object *jnav = json_object_new_array();
    json_object *jback = json_object_new_object();
    json_object_object_add(jback, "label", json_object_new_string(tr(_lang,"Back").c_str()));
    json_object_object_add(jback, "url", json_object_new_string(Args->config->buildurl("settings/staticpage").c_str()));
    json_object_array_add(jnav, jback);
    json_object_object_add(jroot, "nav", jnav);

    return jroot;
}

json_object* blogi::StaticPage::delPage(const int tid,libhttppp::HttpRequest& req){
    std::string _lang = getLang(*Args->database[tid]);
    char url[512];
    uuid::uuid id;
    dbpp::SQL sql;
    dbpp::DBResult res;

    bool confirmed=false;

    libhttppp::HttpForm form;
    form.parse(req);

    for(const auto &cdat : form.urlData()){
        if(cdat.key=="pageid"){
            size_t pgidlen=cdat.value.length();
            id.parse(cdat.value.c_str());
        }else if(cdat.key=="confirmed"){
            if(cdat.value=="true")
                confirmed=true;
        }
    }

    json_object *jroot = json_object_new_object();

    if(confirmed){
        sql << "DELETE FROM static_content WHERE id='" << id.c_str() << "'";
        Args->database[tid]->exec(sql,res);

        std::string msg = std::string("page with id ") + id.c_str() + " is removed !";
        json_object_object_add(jroot, "title", json_object_new_string(tr(_lang,"Static Pages").c_str()));
        json_object_object_add(jroot, "message", json_object_new_string(msg.c_str()));
        json_object_object_add(jroot, "message_type", json_object_new_string("success"));

        json_object *jnav = json_object_new_array();
        json_object *jback = json_object_new_object();
        json_object_object_add(jback, "label", json_object_new_string(tr(_lang,"Back to list").c_str()));
        json_object_object_add(jback, "url", json_object_new_string(Args->config->buildurl("settings/staticpage").c_str()));
        json_object_array_add(jnav, jback);
        json_object_object_add(jroot, "nav", jnav);
    }else{
        std::string msg = std::string("You wan't remove the page with id ") + id.c_str() + "?";
        json_object_object_add(jroot, "title", json_object_new_string(tr(_lang,"Remove Page").c_str()));

        json_object *jconfirm = json_object_new_object();
        json_object_object_add(jconfirm, "message", json_object_new_string(msg.c_str()));
        std::string yesUrl = Args->config->buildurl("settings/staticpage/delpage?") + "pageid=" + id.c_str() + "&confirmed=true";
        json_object_object_add(jconfirm, "yes_url", json_object_new_string(yesUrl.c_str()));
        json_object_object_add(jconfirm, "no_url", json_object_new_string(Args->config->buildurl("settings/staticpage").c_str()));
        json_object_object_add(jroot, "confirm", jconfirm);
    }

    return jroot;
}

json_object* blogi::StaticPage::editPage(const int tid, libhttppp::HttpRequest& req) {
    std::string _lang = getLang(*Args->database[tid]);
    uuid::uuid id;
    dbpp::SQL sql;
    dbpp::DBResult res;
    std::vector<char> sbuf;

    libhttppp::HttpForm form;
    form.parse(req);

    // Helper to create string_view and remove trailing NULL (necessary for this library)
    auto sv_from_vec = [](const std::vector<char>& v) -> std::string_view {
        std::string_view sv(v.data(), v.size());
        if (!sv.empty() && sv.back() == '\0') sv.remove_suffix(1);
        return sv;
    };

    // Lambda to reliably find a Content-Disposition parameter by key (e.g., "name")
    auto find_disposition_param = [&](const libhttppp::HttpForm::MultipartEntry &formData, const std::string &paramKey) -> std::string_view {
        for (const auto &cd : formData.dispositions) {
            if (cd.key == paramKey) {
                return std::string_view(cd.value);
            }
        }
        return std::string_view(); // Return empty view if not found
    };

    try {
        // --- 1) Reliable Page ID Retrieval ---
        for (const auto &curformdat : form.multipartData())
        {
            std::string_view fieldName = find_disposition_param(curformdat, "name");

            if (fieldName == "pageid") {
                std::string_view val = sv_from_vec(curformdat.value);
                uuid::uuid tmp;
                tmp.parse(std::string(val).c_str());
                if (!tmp.empty()) { id = tmp; break; }
            }
            if (!id.empty()) break;
        }

        // Fallback: urlencoded (kept your original logic here)
        if (id.empty()) {
            for (const auto &cdat : form.urlData()) {
                if (cdat.key == "pageid") {
                    id.parse(cdat.value.c_str());
                    break;
                }
            }
        }

        if (id.empty()) {
            libhttppp::HTTPException excep;
            excep[libhttppp::HTTPException::Error] << "Staticpage with this id not found!";
            throw excep;
        }

        // --- 2) Update SQL Building and Execution ---
        sql.clear();
        for (const auto &curformdat : form.multipartData())
        {
            // Use the robust finder to get the field name ("url", "meta", "text")
            std::string_view key = find_disposition_param(curformdat, "name");
            const std::string_view val_sv = sv_from_vec(curformdat.value);
            const std::string val(val_sv);

            auto put_update = [&](const char* col, const char* raw) {
                std::string esc = sql.escaped(sbuf, raw);
                // Batch UPDATE statements for efficiency
                sql << "UPDATE static_content SET " << col << "='" << esc.c_str()
                    << "' WHERE id='" << id.c_str() << "'; ";
            };

            std::string enc;

            if (key == "url") {
                libhtmlpp::HtmlEncode(val, enc);
                put_update("url", enc.c_str());
            } else if (key == "meta") {
                libhtmlpp::HtmlEncode(val, enc);
                put_update("meta", enc.c_str());
            } else if (key == "text") {
                put_update("text", val.c_str());
            }
        }

        if (!sql.empty()) {
            // Execute the batch of UPDATE statements
            Args->database[tid]->exec(sql, res);
            sql.clear();
        }
    } catch (libhtmlpp::HTMLException &e) {
        libhttppp::HTTPException excep;
        excep[libhttppp::HTTPException::Error] << e.what();
        throw excep;
    } catch (const std::exception& e) {
        libhttppp::HTTPException excep;
        excep[libhttppp::HTTPException::Error] << e.what();
        throw excep;
    }

    // --- 3) Re-load and Output JSON Form ---
    sql << "SELECT id,url,meta,text FROM static_content WHERE id='" << id.c_str() << "' LIMIT 1";
    if (Args->database[tid]->exec(sql, res) < 1) return nullptr;

    json_object *jroot = json_object_new_object();
    json_object_object_add(jroot, "title", json_object_new_string(tr(_lang,"Edit Page").c_str()));

    // Nav: back link
    json_object *jnav = json_object_new_array();
    json_object *jback = json_object_new_object();
    json_object_object_add(jback, "label", json_object_new_string(tr(_lang,"Back").c_str()));
    json_object_object_add(jback, "url", json_object_new_string(Args->config->buildurl("settings/staticpage").c_str()));
    json_object_array_add(jnav, jback);
    json_object_object_add(jroot, "nav", jnav);

    // Form
    json_object *jform = json_object_new_object();
    json_object_object_add(jform, "method", json_object_new_string("POST"));
    json_object_object_add(jform, "enctype", json_object_new_string("multipart/form-data"));
    std::string formAction = Args->config->buildurl("settings/staticpage/editpage?pageid=") + std::string(res[0][0]);
    json_object_object_add(jform, "action", json_object_new_string(formAction.c_str()));

    json_object *jfields = json_object_new_array();

    // Hidden pageid
    json_object *jfId = json_object_new_object();
    json_object_object_add(jfId, "name", json_object_new_string("pageid"));
    json_object_object_add(jfId, "type", json_object_new_string("hidden"));
    json_object_object_add(jfId, "value", json_object_new_string(res[0][0]));
    json_object_array_add(jfields, jfId);

    json_object *jfUrl = json_object_new_object();
    json_object_object_add(jfUrl, "name", json_object_new_string("url"));
    json_object_object_add(jfUrl, "label", json_object_new_string(tr(_lang,"Url").c_str()));
    json_object_object_add(jfUrl, "type", json_object_new_string("text"));
    json_object_object_add(jfUrl, "value", json_object_new_string(res[0][1]));
    json_object_array_add(jfields, jfUrl);

    json_object *jfMeta = json_object_new_object();
    json_object_object_add(jfMeta, "name", json_object_new_string("meta"));
    json_object_object_add(jfMeta, "label", json_object_new_string(tr(_lang,"Meta").c_str()));
    json_object_object_add(jfMeta, "type", json_object_new_string("textarea"));
    json_object_object_add(jfMeta, "value", json_object_new_string(res[0][2]));
    json_object_array_add(jfields, jfMeta);

    json_object *jfText = json_object_new_object();
    json_object_object_add(jfText, "name", json_object_new_string("text"));
    json_object_object_add(jfText, "label", json_object_new_string(tr(_lang,"Content").c_str()));
    json_object_object_add(jfText, "type", json_object_new_string("textarea"));
    json_object_object_add(jfText, "value", json_object_new_string(res[0][3]));
    json_object_array_add(jfields, jfText);

    json_object_object_add(jform, "fields", jfields);
    json_object_object_add(jform, "submit", json_object_new_string(tr(_lang,"Save").c_str()));
    json_object_object_add(jroot, "form", jform);

    return jroot;
}

void blogi::StaticPage::uploadPage(const int tid,libhttppp::HttpRequest& req, libhtmlpp::HtmlString& setdiv){
}



bool blogi::StaticPage::Controller(const int tid,libhttppp::HttpRequest &req,libhtmlpp::HtmlElement *page,const std::string &sessionid){
    if (req.getRequestURL().compare(0,Args->config->buildurl("staticpage").length(),Args->config->buildurl("staticpage"))==0){
        std::string surl=req.getRequestURL().substr(Args->config->buildurl("staticpage/").length(),
                                                                                    req.getRequestURL().length()-Args->config->buildurl("staticpage/").length());

        libhttppp::HTTPException excep;

        dbpp::SQL sql;
        dbpp::DBResult res;
        std::vector<char> sbuf;

        sql << "select url,text,meta from static_content where url='" << sql.escaped(sbuf,surl.c_str()) << "' LIMIT 1";

        if (Args->database[tid]->exec(sql,res)<1) {
            excep[libhttppp::HTTPException::Error] << "Staticpage with this url not found!";
            throw excep;
        }

        std::string text = res[0][1] ? res[0][1] : "";
        std::string pageTitle = res[0][0] ? res[0][0] : "";
        RenderPublished(tid, text, nullptr, *page);

         libhtmlpp::HtmlElement canon("link");
         std::stringstream href;
         href << Args->config->getsiteurl() << "/staticpage/" << res[0][0];
         canon.setAttribute("rel","canonical");
         canon.setAttribute("href",href.str());
         if(page->getElementbyTag("head"))
            page->getElementbyTag("head")->appendChild(canon);

        libhtmlpp::HtmlString out;
        Args->theme->printSite(tid,out,page,req.getRequestURL(),Args->auth->isLoggedIn(tid,req,sessionid),res[0][2],pageTitle.c_str());

        libhttppp::HttpResponse resp;
        resp.setState(HTTP200);
        resp.setContentType("text/html");
        sendCompressed(req,resp,out.c_str(),out.length());
        return true;
    }
    return false;
}

void blogi::StaticPage::Search(const int tid, const char* word, blogi::SearchRet& result, const std::string &sessionid){
    std::vector<char> buf;
    dbpp::SQL sql;
    dbpp::DBResult res;

    sql << "SELECT id,url,meta,text FROM static_content WHERE text @@ to_tsquery('" << sql.escaped(buf,word) <<"') OR meta @@ to_tsquery('" << sql.escaped(buf,word) <<"');";

    int count=Args->database[tid]->exec(sql,res);

    for (int i=0; i < count; ++i) {
        SearchRet::Data data;
        data.title=res[i][1];

        std::copy(res[i][2],res[i][2]+strlen(res[i][2]),std::back_inserter(data.text));

        data.text.push_back('\0');

        char url[512];
        std::string surl=Args->config->buildurl("staticpage/");

        surl+=res[i][1];

        data.anchor=surl;

        result.push_back(data);

    }

}


const std::string  blogi::StaticPage::getAuthor(){
    return "Jan Koester";
}

const std::string  blogi::StaticPage::getName(){
    return "staticpage";
}

const std::string  blogi::StaticPage::getVersion(){
    return "20230809";
}

bool blogi::StaticPage::canPublish(){
    return true;
}

void blogi::StaticPage::RenderPublished(const int /*tid*/, const std::string &html,
                                        json_object * /*params*/, libhtmlpp::HtmlElement &page){
    libhtmlpp::HtmlElement *main = page.getElementbyID("main");
    if (!main) return;

    libhtmlpp::HtmlString condat;
    condat << "<div id=\"content\" class=\"staticpage\">"
           << html
           << "</div>";
    try {
        main->insertChild(condat.parse());
    } catch (libhtmlpp::HTMLException &e) {
        std::cerr << "staticpage: RenderPublished HTML error: " << e.what() << std::endl;
        libhtmlpp::HtmlElement content("div");
        content.setAttribute("id","content");
        content.setAttribute("class","staticpage");
        content.appendChild(libhtmlpp::TextElement(html.c_str()));
        main->appendChild(content);
    }
}

json_object* blogi::StaticPage::getPublishForm(const int tid){
    std::string _lang = getLang(*Args->database[tid]);
    json_object *fields = json_object_new_array();

    json_object *f_url = json_object_new_object();
    json_object_object_add(f_url, "key", json_object_new_string("url"));
    json_object_object_add(f_url, "label", json_object_new_string(tr(_lang,"URL Slug").c_str()));
    json_object_object_add(f_url, "type", json_object_new_string("text"));
    json_object_object_add(f_url, "required", json_object_new_boolean(1));
    json_object_object_add(f_url, "placeholder", json_object_new_string(tr(_lang,"my-page-url").c_str()));
    json_object_array_add(fields, f_url);

    json_object *f_meta = json_object_new_object();
    json_object_object_add(f_meta, "key", json_object_new_string("meta"));
    json_object_object_add(f_meta, "label", json_object_new_string(tr(_lang,"Meta / Keywords").c_str()));
    json_object_object_add(f_meta, "type", json_object_new_string("text"));
    json_object_object_add(f_meta, "placeholder", json_object_new_string(tr(_lang,"keyword1, keyword2").c_str()));
    json_object_array_add(fields, f_meta);

    json_object *f_method = json_object_new_object();
    json_object_object_add(f_method, "key", json_object_new_string("method"));
    json_object_object_add(f_method, "label", json_object_new_string(tr(_lang,"Publish Method").c_str()));
    json_object_object_add(f_method, "type", json_object_new_string("select"));
    json_object *method_opts = json_object_new_array();
    json_object *m_default = json_object_new_object();
    json_object_object_add(m_default, "value", json_object_new_string("none"));
    json_object_object_add(m_default, "label", json_object_new_string(tr(_lang,"Only publish static page").c_str()));
    json_object_array_add(method_opts, m_default);
    json_object *m_nav = json_object_new_object();
    json_object_object_add(m_nav, "value", json_object_new_string("add_to_navbar"));
    json_object_object_add(m_nav, "label", json_object_new_string(tr(_lang,"Publish and add to navbar").c_str()));
    json_object_array_add(method_opts, m_nav);
    json_object_object_add(f_method, "options", method_opts);
    json_object_array_add(fields, f_method);

    json_object *f_navlabel = json_object_new_object();
    json_object_object_add(f_navlabel, "key", json_object_new_string("navbar_label"));
    json_object_object_add(f_navlabel, "label", json_object_new_string(tr(_lang,"Navbar Label").c_str()));
    json_object_object_add(f_navlabel, "type", json_object_new_string("text"));
    json_object_object_add(f_navlabel, "placeholder", json_object_new_string(tr(_lang,"Page title in navbar").c_str()));
    json_object_array_add(fields, f_navlabel);

    json_object *f_navbar = json_object_new_object();
    json_object_object_add(f_navbar, "key", json_object_new_string("navbar_id"));
    json_object_object_add(f_navbar, "label", json_object_new_string(tr(_lang,"Target Navbar").c_str()));
    json_object_object_add(f_navbar, "type", json_object_new_string("select"));
    json_object *navbar_opts = json_object_new_array();
    json_object *nav_auto = json_object_new_object();
    json_object_object_add(nav_auto, "value", json_object_new_string(""));
    json_object_object_add(nav_auto, "label", json_object_new_string(tr(_lang,"Auto (first navbar)").c_str()));
    json_object_array_add(navbar_opts, nav_auto);

    try {
        dbpp::SQL sql;
        dbpp::DBResult res;
        sql << "SELECT id,name FROM navbar ORDER BY id";
        int count = Args->database[tid]->exec(sql, res);
        for (int i = 0; i < count; ++i) {
            json_object *nav_opt = json_object_new_object();
            json_object_object_add(nav_opt, "value", json_object_new_string(res[i][0] ? res[i][0] : ""));
            json_object_object_add(nav_opt, "label", json_object_new_string(res[i][1] ? res[i][1] : ""));
            json_object_array_add(navbar_opts, nav_opt);
        }
    } catch (...) {}

    json_object_object_add(f_navbar, "options", navbar_opts);
    json_object_array_add(fields, f_navbar);

    return fields;
}

void blogi::StaticPage::Publish(const int tid, const std::string &html, json_object *params, json_object *response){
    std::string _lang = getLang(*Args->database[tid]);
    std::vector<char> sbuf;
    json_object *val_obj = nullptr;

    std::string url, meta, method, navbarLabel;
    int navbarId = -1;

    if (params) {
        if (json_object_object_get_ex(params, "url", &val_obj)) {
            const char *v = json_object_get_string(val_obj);
            if (v) url = v;
        }
        if (json_object_object_get_ex(params, "meta", &val_obj)) {
            const char *v = json_object_get_string(val_obj);
            if (v) meta = v;
        }
        if (json_object_object_get_ex(params, "method", &val_obj)) {
            const char *v = json_object_get_string(val_obj);
            if (v) method = v;
        }
        if (json_object_object_get_ex(params, "navbar_label", &val_obj)) {
            const char *v = json_object_get_string(val_obj);
            if (v) navbarLabel = v;
        }
        if (json_object_object_get_ex(params, "navbar_id", &val_obj)) {
            if (json_object_is_type(val_obj, json_type_string)) {
                const char *v = json_object_get_string(val_obj);
                if (v && *v) navbarId = atoi(v);
            } else {
                navbarId = json_object_get_int(val_obj);
            }
        }
    }

    if (method.empty()) method = "none";

    if (url.empty()) {
        json_object_object_add(response, "status", json_object_new_string("error"));
        json_object_object_add(response, "message", json_object_new_string(tr(_lang,"URL slug is required.").c_str()));
        return;
    }

    try {
        dbpp::SQL sql;
        dbpp::DBResult res;

        // Check if a page with this URL already exists
        sql << "SELECT id FROM static_content WHERE url='" << dbpp::SQL::escaped(sbuf, url.c_str()) << "' LIMIT 1";
        int existing = Args->database[tid]->exec(sql, res);

        if (existing > 0) {
            // Update existing page
            std::string pageId = res[0][0];
            sql.clear();
            sql << "UPDATE static_content SET text='" << dbpp::SQL::escaped(sbuf, html.c_str())
                << "', meta='" << dbpp::SQL::escaped(sbuf, meta.c_str())
                << "' WHERE id='" << pageId.c_str() << "';";
            Args->database[tid]->exec(sql, res);

            json_object_object_add(response, "status", json_object_new_string("success"));
            json_object_object_add(response, "message", json_object_new_string(tr(_lang,"Static page updated.").c_str()));
            std::string fullUrl = Args->config->getprefix() + "/staticpage/" + url;
            json_object_object_add(response, "url", json_object_new_string(fullUrl.c_str()));
        } else {
            // Insert new page
            uuid::uuid id;
            id.generate();

            sql.clear();
            sql << "INSERT INTO static_content (id,url,meta,text) VALUES('"
                << id.c_str() << "','"
                << dbpp::SQL::escaped(sbuf, url.c_str()) << "','"
                << dbpp::SQL::escaped(sbuf, meta.c_str()) << "','"
                << dbpp::SQL::escaped(sbuf, html.c_str()) << "');";
            Args->database[tid]->exec(sql, res);

            json_object_object_add(response, "status", json_object_new_string("success"));
            json_object_object_add(response, "message", json_object_new_string(tr(_lang,"Static page published.").c_str()));
            std::string fullUrl = Args->config->getprefix() + "/staticpage/" + url;
            json_object_object_add(response, "url", json_object_new_string(fullUrl.c_str()));
        }

        if (method == "add_to_navbar") {
            if (navbarId <= 0) {
                sql.clear();
                res.clear();
                sql << "SELECT id FROM navbar ORDER BY id LIMIT 1";
                if (Args->database[tid]->exec(sql, res) > 0) {
                    navbarId = atoi(res[0][0]);
                }
            }

            if (navbarId > 0) {
                std::string navUrl = Args->config->buildurl("staticpage/") + url;
                std::string navName = navbarLabel.empty() ? url : navbarLabel;

                sql.clear();
                res.clear();
                sql << "SELECT id FROM navbar_items WHERE navbar_id=" << navbarId
                    << " AND url='" << dbpp::SQL::escaped(sbuf, navUrl.c_str()) << "' LIMIT 1";
                if (Args->database[tid]->exec(sql, res) < 1) {
                    sql.clear();
                    res.clear();
                    sql << "INSERT INTO navbar_items (name,url,type,navbar_id) VALUES ('"
                        << dbpp::SQL::escaped(sbuf, navName.c_str()) << "','"
                        << dbpp::SQL::escaped(sbuf, navUrl.c_str()) << "',0,"
                        << navbarId << ")";
                    Args->database[tid]->exec(sql, res);
                }
            } else {
                json_object_object_add(response, "navbar_warning",
                    json_object_new_string(tr(_lang,"No navbar found. Static page was published without navbar entry.").c_str()));
            }
        }
    } catch (std::exception &e) {
        json_object_object_add(response, "status", json_object_new_string("error"));
        json_object_object_add(response, "message", json_object_new_string(e.what()));
    } catch (...) {
        json_object_object_add(response, "status", json_object_new_string("error"));
        json_object_object_add(response, "message", json_object_new_string(tr(_lang,"Unknown error during static page publish.").c_str()));
    }
}
