/*******************************************************************************
 * 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 <cstring>
#include <string>

#include <dbpp/database.h>
#include <json-c/json.h>

#include "plugin.h"
#include "theme.h"
#include "conf.h"
#include "cookie.h"
#include <i18n.h>

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

#define gtmid "d5ca7d8c-1490-11f0-91c5-3cecefce9ac2"
#define g4aid "96143d1c-14f7-11f0-b2e9-3cecefce9ac2"

namespace blogi {
    class Gtm : public PluginApi {
    public:
        Gtm(){
        }

        ~Gtm(){
        }

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

        const std::string getVersion(){
            return "0.2";
        }

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

        void initPlugin(){
            registerGPO("5ccbc05e-30bf-46ff-a9ff-cc964803b476",
                        "Blogi: GTM Settings",
                        "Controls access to Google Tag Manager settings",
                        true);

            std::string _lang = getLang(*Args->database[0]);
            try{
                getConfig(*Args->database[0],"GOOGLEGTM",GTM);
                getConfig(*Args->database[0],"GOOGLE4A",G4A);
            }catch(...){}
            CookieBanner cookie(*Args->database[0]);
            uuid::uuid id(gtmid);
            cookie.addRule(id,tr(_lang,"Google Tag Manager").c_str(),tr(_lang,"This cookie is required for Tag Manager to improve the website.").c_str(),false);
            id=g4aid;
            cookie.addRule(id,tr(_lang,"Google Analytics").c_str(),tr(_lang,"This cookie is required for Analytics to improve the website.").c_str(),false);
            return;
        }

        void Rendering(const int tid,libhttppp::HttpRequest &req,libhtmlpp::HtmlElement* curpage,int type,const std::string &sessionid){
            libhtmlpp::HtmlElement *head=curpage->getElementbyTag("head");

            CookieBanner cookie(*Args->database[tid]);

            bool gtmGranted = cookie.getPermission(sessionid,gtmid);
            bool ga4Granted = cookie.getPermission(sessionid,g4aid);

            if(head){
                libhtmlpp::HtmlString headjs;

                // Always define dataLayer and gtag function + consent defaults (denied)
                headjs << "<script>"
                       << "window.dataLayer = window.dataLayer || [];"
                       << "function gtag(){dataLayer.push(arguments);}"
                       << "gtag('consent', 'default', {"
                       << "'ad_storage': 'denied',"
                       << "'ad_user_data': 'denied',"
                       << "'ad_personalization': 'denied',"
                       << "'analytics_storage': 'denied',"
                       << "'functionality_storage': 'denied',"
                       << "'personalization_storage': 'denied',"
                       << "'security_storage': 'denied'"
                       << "});"
                       << "</script>";

                // Lazy load: defer GTM/GA4 until user interaction or timeout
                if(gtmGranted || ga4Granted){
                    headjs << "<script>"
                           << "(function(){"
                           << "var loaded=false;"
                           << "function loadGTM(){";

                    if(ga4Granted){
                        headjs << "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(gtmGranted){
                        headjs << "(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':"
                               << "new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],"
                               << "j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src="
                               << "'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);"
                               << "})(window,document,'script','dataLayer','" << GTM << "');";
                    }

                    if(ga4Granted){
                        headjs << "var s=document.createElement('script');"
                               << "s.async=true;s.src='https://www.googletagmanager.com/gtag/js?id=" << G4A << "';"
                               << "document.head.appendChild(s);"
                               << "gtag('js',new Date());"
                               << "gtag('config','" << G4A << "');";
                    }

                    headjs << "}"  // end loadGTM
                           << "function trigger(){"
                           << "if(loaded)return;loaded=true;"
                           << "loadGTM();"
                           << "['scroll','click','keydown','touchstart'].forEach(function(e){"
                           << "document.removeEventListener(e,trigger,{passive:true});"
                           << "});"
                           << "}"
                           << "['scroll','click','keydown','touchstart'].forEach(function(e){"
                           << "document.addEventListener(e,trigger,{passive:true});"
                           << "});"
                           << "setTimeout(trigger,5000);"
                           << "})();"
                           << "</script>";
                }

                if(!headjs.empty())
                    head->appendChild(headjs.parse());
            }

            libhtmlpp::HtmlElement *body=curpage->getElementbyTag("body");
            if(gtmGranted && body){
                libhtmlpp::HtmlString gtmjs;
                gtmjs << "<!-- Google Tag Manager (noscript) -->"
                          <<  "<noscript><iframe src=\"https://www.googletagmanager.com/ns.html?id="
                          << GTM << "\""
                          <<  " height=\"0\" width=\"0\" style=\"display:none;visibility:hidden\"></iframe></noscript>"
                          << "<!-- End Google Tag Manager (noscript) -->";

                body->appendChild(gtmjs.parse());
            }
        }

        bool haveSettings(){
            return true;
        }

        json_object* Settings(const int tid,libhttppp::HttpRequest &req,const std::string &sessionid){
            std::string _lang = getLang(*Args->database[tid]);
            bool saved = false;

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

            for(const auto &cur : form.urlData()){
                if(cur.key=="googlegtm"){
                    setConfig(*Args->database[tid],"GOOGLEGTM",cur.value);
                    saved = true;
                }else if(cur.key=="google4a"){
                    setConfig(*Args->database[tid],"GOOGLE4A",cur.value);
                    saved = true;
                }
            }

            if(saved){
                try{ getConfig(*Args->database[tid],"GOOGLEGTM",GTM); }catch(...){}
                try{ getConfig(*Args->database[tid],"GOOGLE4A",G4A); }catch(...){}
            }

            json_object *jroot = json_object_new_object();
            json_object_object_add(jroot, "title", json_object_new_string(tr(_lang,"Google Tag Manager").c_str()));

            if(saved){
                json_object_object_add(jroot, "message", json_object_new_string(tr(_lang,"Saved!").c_str()));
                json_object_object_add(jroot, "message_type", json_object_new_string("success"));
            }

            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/gtm").c_str()));

            json_object *jfields = json_object_new_array();

            auto addField = [&](const char *name, const char *label, const char *type, const std::string &value){
                json_object *jf = json_object_new_object();
                json_object_object_add(jf, "name", json_object_new_string(name));
                json_object_object_add(jf, "label", json_object_new_string(label));
                json_object_object_add(jf, "type", json_object_new_string(type));
                json_object_object_add(jf, "value", json_object_new_string(value.c_str()));
                json_object_array_add(jfields, jf);
            };

            addField("googlegtm", "GTM Container ID", "text", GTM);
            addField("google4a",  "Google Analytics 4 ID", "text", G4A);

            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;
        }

        bool Controller(const int tid,libhttppp::HttpRequest &req,libhtmlpp::HtmlElement *page,const std::string &sessionid){
            return false;
        }

    private:
        std::string GTM;
        std::string G4A;
    };
};

EXPORT blogi::PluginApi* create() {
    return new blogi::Gtm();
}

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

