<?php

use \Symfony\Component\Yaml\Yaml;
use \Symfony\Component\Yaml\Exception\ParseException;


require_once(MH_DIR_FS_CATALOG . MH_ROOT_PATH . 'common/classes/mailbeez_cache.php');

require_once(MH_DIR_FS_CATALOG . MH_ROOT_PATH . 'common/classes/mailbeez_mailer.php');
require_once(MH_DIR_FS_CATALOG . MH_ROOT_PATH . 'common/classes/googleanalytics.php');
require_once(MH_DIR_FS_CATALOG . MH_ROOT_PATH . 'common/classes/analytics_piwik.php');
require_once(MH_DIR_FS_CATALOG . MH_ROOT_PATH . 'common/classes/analytics_econda.php');
require_once(MH_DIR_FS_CATALOG . MH_ROOT_PATH . 'common/classes/analytics_custom.php');
require_once(MH_DIR_FS_CATALOG . MH_ROOT_PATH . 'common/functions/compatibility.php');

class beez extends mailbeez_cache
{
    // Email Group constants for module categorization
    // These mirror the group strings used across legacy /mailbeez modules
    public const GROUP_ONBOARDING          = 'onboarding';
    public const GROUP_TRANSACTIONAL       = 'transactional';
    public const GROUP_RETENTION           = 'retention';
    public const GROUP_CONVERSION          = 'conversion';
    public const GROUP_LIFECYCLE           = 'lifecycle';

    public const GROUP_REVIEW_FEEDBACK     = 'review_feedback';
    public const GROUP_COUPON_INCENTIVE    = 'coupon_incentive';
    public const GROUP_NOTIFICATION        = 'notification';
    public const GROUP_NEWSLETTER          = 'newsletter';
    public const GROUP_SERVICE             = 'service';
    public const GROUP_CONFIGURATION       = 'configuration';
    public const GROUP_ANALYTICS      = 'analytics';
    public const GROUP_OTHER = 'other';
    
    // Template Tags for categorization
    public const TAG_MODERN     = 'modern';
    public const TAG_SAAS       = 'saas';
    public const TAG_RESPONSIVE = 'responsive';

    var $module = null;
    var $version = null;
    var $code = false;
    var $enabled = false;
    var $status_key = null;
    var $auto_install = false;
    var $advanced = false;
    var $mlbz = false;
    var $mlbz_advanced = false;
    var $versionScope = null;
    var $session_cache_time; // cache time in sec // todo: make configurable
    var $app_cache_time; // cache time in sec // todo: make configurable
    var $is_submodule_of = null;
    var $show_submodule_status = null;
    var $display_as_submodule_of = null; // display a submodule
    var $has_submodules = false;

    var $show_submodules = null;
    var $allow_group_filter_check = null;
    var $subscription = null;
    var $cert_check = null;

    var $is_editable;

    var $noblock;

    var $certificate;
    var $certificate_key;
    var $on_cfg_save_clear_template_c;

    var $mb_chk;
    public $_check;
    public $description;
    public $sort_order;
    public $title;
    public $label;
    public $required_mb_version;
    public $admin_action_plugins_path;
    public $admin_action_plugins;
    public $common_admin_action_plugins;
    public $icon;
    public $description_image;
    public $documentation_root;
    public $documentation_key;
    public $hidden;
    public $is_configurable;
    public $removable;

    public $stealth;

    private $blueprintPath;


    public $group = self::GROUP_OTHER;
    
    public $svg_icon = null; // svg icon
    public $color = ''; // Tailwind class

    public $beez_ui_ready = false;

    /**
     * Returns a mapping of group identifiers to localized labels.
     * Example: [ self::GROUP_ONBOARDING => mh_lng('MAILBEEZ_GROUP_LABEL_ONBOARDING') , ... ]
     *
     * The method discovers all class constants starting with "GROUP_" and
     * builds their translation keys automatically. If a translation is missing,
     * a humanized fallback based on the group value is used (e.g. "review_feedback" ? "Review Feedback").
     */
    public static function groupLabels(): array
    {
        $labels = [];
        try {
            $ref = new \ReflectionClass(__CLASS__);
            foreach ($ref->getConstants() as $name => $value) {
                if (strpos($name, 'GROUP_') === 0) {
                    $suffix  = substr($name, strlen('GROUP_'));
                    $i18nKey = 'MAILBEEZ_GROUP_LABEL_' . $suffix;
                    $fallback = ucwords(str_replace('_', ' ', (string) $value));
                    $labels[$value] = mh_lng($i18nKey, $fallback);
                }
            }
        } catch (\ReflectionException $e) {
            // Minimal hardcoded fallback if Reflection is unavailable
            $labels = [
                self::GROUP_ONBOARDING       => mh_lng('MAILBEEZ_GROUP_LABEL_ONBOARDING', 'Onboarding'),
                self::GROUP_TRANSACTIONAL    => mh_lng('MAILBEEZ_GROUP_LABEL_TRANSACTIONAL', 'Transactional'),
                self::GROUP_RETENTION        => mh_lng('MAILBEEZ_GROUP_LABEL_RETENTION', 'Retention'),
                self::GROUP_CONVERSION       => mh_lng('MAILBEEZ_GROUP_LABEL_CONVERSION', 'Conversion'),
                self::GROUP_LIFECYCLE        => mh_lng('MAILBEEZ_GROUP_LABEL_LIFECYCLE', 'Lifecycle'),
                self::GROUP_REVIEW_FEEDBACK  => mh_lng('MAILBEEZ_GROUP_LABEL_REVIEW_FEEDBACK', 'Review Feedback'),
                self::GROUP_COUPON_INCENTIVE => mh_lng('MAILBEEZ_GROUP_LABEL_COUPON_INCENTIVE', 'Coupon Incentive'),
                self::GROUP_NOTIFICATION     => mh_lng('MAILBEEZ_GROUP_LABEL_NOTIFICATION', 'Notification'),
                self::GROUP_OTHER            => mh_lng('MAILBEEZ_GROUP_LABEL_OTHER', 'Other'),
            ];
        }
        return $labels;
    }

    /**
     * Returns a mapping of tag identifiers to localized labels.
     * Discovers all constants starting with "TAG_" and builds translation keys.
     */
    public static function tagLabels(): array
    {
        $labels = [];
        try {
            $ref = new \ReflectionClass(__CLASS__);
            foreach ($ref->getConstants() as $name => $value) {
                if (strpos($name, 'TAG_') === 0) {
                    $suffix  = substr($name, strlen('TAG_'));
                    $i18nKey = 'MAILBEEZ_TAG_LABEL_' . $suffix;
                    $fallback = ucwords(str_replace('_', ' ', (string) $value));
                    $labels[$value] = mh_lng($i18nKey, $fallback);
                }
            }
        } catch (\ReflectionException $e) {
            $labels = [
                self::TAG_MODERN     => mh_lng('MAILBEEZ_TAG_LABEL_MODERN', 'Modern'),
                self::TAG_SAAS       => mh_lng('MAILBEEZ_TAG_LABEL_SAAS', 'SaaS'),
                self::TAG_RESPONSIVE => mh_lng('MAILBEEZ_TAG_LABEL_RESPONSIVE', 'Responsive'),
            ];
        }
        return $labels;
    }

    // ---- Metadata getters for module loader ----
    /** @var string|null */
    private $flowBuilderService = null;
    public function metaGroup(): string { return is_string($this->group) && $this->group !== '' ? $this->group : 'Other'; }
    public function metaIcon(): ?string { return isset($this->icon) && is_string($this->icon) ? $this->icon : null; }
    public function metaSvgIcon(): ?string { return isset($this->svg_icon) && is_string($this->svg_icon) ? $this->svg_icon : null; }
    public function metaColor(): string { return isset($this->color) && is_string($this->color) ? $this->color : ''; }
    public function metaTitle(): string { return isset($this->label) && is_string($this->label) ? $this->label  : ( isset($this->title) && is_string($this->title) ? $this->title : ''); }
    public function metaDescription(): string { return isset($this->description) && is_string($this->description) ? $this->description : ''; }
    public function blueprintPath(): ?string { $p = $this->_getBlueprintPath(); return is_string($p) && $p !== '' ? $p : null; }
    public function configurationKeys(): array { $k = $this->keys(); return is_array($k) ? $k : []; }

    // FlowBuilder bootstrapper for reuse in child classes
    public function getFlowBuilderService(): ?string {
        $cls = $this->flowBuilderService ?: '\\Components\\CampaignFlow\\FlowBuilder';
        if (is_string($cls) && class_exists($cls)) {
            return $cls;
        }
        return null;
    }

    // Returns a FlowBuilder instance or null if unavailable
    protected function initFlowBuilder($module): ?object {
        $cls = $this->getFlowBuilderService();
        if (!$cls || !method_exists($cls, 'make')) {
            return null;
        }
        return $cls::make($module);
    }

    public function flow() {
        // Use short aliases for readability
        $B = '\\Components\\Base';
        
        // Initialize FlowBuilder
        $flowBuilder = $this->initFlowBuilder($this->module);
        if (!$flowBuilder) {
            return [];
        }
        
        return $flowBuilder
            ->trigger('New Subscriber', ['icon' => $B::SVG_TRIGGER, 'color' => 'blue'])
            ->subtitle('Trigger')
            ->wait('1 day')
            ->step('w1', 'Welcome Email 1', ['icon' => $B::SVG_MAIL, 'color' => 'indigo'])
            ->subtitle('Step 1')
            ->wait('3 days')
            ->step('w2', 'Welcome Email 2', ['icon' => $B::SVG_MAIL, 'color' => 'yellow'])
            ->subtitle('Step 2')
            ->wait('7 days')
            ->step('w3', 'Welcome Email 3', ['icon' => $B::SVG_MAIL, 'color' => 'green'])
            ->subtitle('Step 3')
            ->addButton()
            ->toArray();
    }

    function __construct()
    {
        parent::__construct();

        $this->session_cache_time = 15 * 60;
        $this->app_cache_time = 15 * 60;

        // Attach FlowBuilder service if available
        if (class_exists('\\Components\\CampaignFlow\\FlowBuilder')) {
            $this->flowBuilderService = '\\Components\\CampaignFlow\\FlowBuilder';
        }

        if ($this->status_key) {
            define($this->status_key, mh_cfg($this->status_key));
        }

    }

    function boot()
    {
        $this->booted();
        if ($this->isSelected()) {
            $this->init();
        }
        return true;
    }

    function isSelected() {
        if (mh_get('module') == $this->module) {
            return true;
        }
        if (stristr(mh_get('app_path', ''),  $this->module . '/')) {
            return true;
        }

        return false;
    }

    function booted()
    {
        $this->autoinstall();
        if (mh_cfg($this->status_key)) {
            $this->check_update();
        };
    }

    function init() {
        return true;
    }

    function keys()
    {
        // to be done by instance
    }

    function install()
    {
        // to be done by instance
    }

    function onSave()
    {
        // to be done by instance
    }

    function check()
    {
        if (!isset($this->_check)) {
            $config_item = mh_read_config_item($this->status_key);
            $this->_check = $config_item['result'];
        }
        return $this->_check;
    }


    function check_update()
    {
        $installed_version = $this->get_installed_version($this->module);

        if ((int)$installed_version > 0) {
            if ($installed_version < $this->version) {
                $action = mh_get('action');
                if ($action != 'config_update_ok') {
                    // avoid loop with config cache installed
                    $this->update($installed_version);
                }
            }
        }
    }


    function autoinstall()
    {
        if ($this->auto_install) {
            if (!mh_cfg($this->status_key)) {
                $this->install();
            }
            $this->removable = false;
        }
    }

    function isEnabled()
    {
        return $this->enabled;
    }

    function get_module_id()
    {
        if ($this->is_submodule_of != '') {
            return $this->is_submodule_of . '/submodules/' . $this->code;
        }
        return $this->code;
    }


    function getVersionScopeToken()
    {
        return $this->versionScope;
    }

    function getVersionInformation()
    {
        return mh_cfg($this->versionScope, false);
    }

    function get_installed_version($module)
    {
        if (function_exists('mh_get_installed_version_from_list')) {
            return mh_get_installed_version_from_list($this->getVersionInformation(), (string)$module);
        }

        $installed_modules_list = explode(';', (string)$this->getVersionInformation());
        foreach ($installed_modules_list as $inst_modules) {
            if (!stristr($inst_modules, '|')) {
                continue;
            }
            $parts = explode('|', $inst_modules);
            $installed_module = isset($parts[0]) ? $parts[0] : '';
            $installed_module_version = isset($parts[1]) ? $parts[1] : '';
            $installed_module_check = isset($parts[2]) ? trim((string)$parts[2]) : null;

            if ($installed_module != $module) {
                continue;
            }

            // Legacy lists may not have an explicit check flag. Treat missing/empty check as installed.
            if ($installed_module_check === null || $installed_module_check === '' || (int)$installed_module_check > 0) {
                return str_replace(',', '.', $installed_module_version);
            }
        }
        return false;
    }

    function update_installed_version_list($version_information, $module, $version)
    {
        if (function_exists('mh_update_installed_version_list')) {
            return mh_update_installed_version_list($version_information, (string)$module, $version);
        }

        $installed_modules_list = explode(';', (string)$version_information);
        $updated_modules_list = array();

        foreach ($installed_modules_list as $inst_modules) {
            $inst_modules = trim($inst_modules);
            if ($inst_modules === '') {
                continue;
            }

            $parts = explode('|', $inst_modules);
            $installed_module = isset($parts[0]) ? $parts[0] : '';

            if ($installed_module == $module) {
                if (isset($parts[1])) {
                    $parts[1] = (string)$version;
                } else {
                    $parts[] = (string)$version;
                }
                $inst_modules = implode('|', $parts);
            }

            $updated_modules_list[] = $inst_modules;
        }

        return implode(';', $updated_modules_list);
    }

    function dbdate($day)
    {
        $rawtime = strtotime(-1 * (int)$day . " days");
        $ndate = date("Ymd", $rawtime);
        return $ndate;
    }

    function remove()
    {
        $remove_keys = $this->keys();
        return $this->_remove($remove_keys);
    }


    function _remove($remove_keys)
    {
        $remove_keys = $this->addRemoveXtcKeys($remove_keys);
        return mh_delete_config_values($remove_keys);
    }

    function addRemoveXtcKeys($remove_keys)
    {
        if (MH_PLATFORM == 'xtc') {
            // remove additional fields
            $xtc_text_keys = array();
            foreach ($this->keys() as $k => $key_name) {
                $xtc_text_keys[] = $key_name . '_TITLE';
                $xtc_text_keys[] = $key_name . '_DESC';
            }
            $remove_keys = array_merge($xtc_text_keys, $this->keys());
        }
        return $remove_keys;
    }


    // added mailbeez V2.803

    function mhcc($a)
    {
        return mhcc($a);
    }


    static function formatStatusMsg($msg, $status = 'error')
    {
        if ($status == 'error') {
            $color = 'red';
        } elseif ($status == 'success') {
            $color = 'green';
        }
        return '<div style="margin-top: 10px;margin-bottom: 10px;padding: 10px; border: 1px solid ' . $color . '; font-weight: bold; color: ' . $color . '; text-align: center">' . $msg . '</div>';

    }

    function modeSaas()
    {
        return getenv('SAAS_ENV');
    }

    function isMlbz()
    {
        return true;
        if (getenv('SAAS_ENV')) {
            return $this->mlbz;
        }
        return true;
    }


    function update($installed_version = null)
    {
        if (!defined($this->getVersionScopeToken()) || $this->getVersionInformation() == $this->getVersionScopeToken()) {
            return false;
        }

        $updated = $this->update_installed_version_list($this->getVersionInformation(), $this->module, $this->version);

        mh_insert_config_value(array('configuration_title' => 'Installed Modules',
            'configuration_key' => $this->getVersionScopeToken(),
            'configuration_value' => $updated,
            'configuration_description' => 'This is automatically updated. No need to edit.',
            'set_function' => ''
        ), true);

        mh_reset_config_cache();
    }

    private function readBlueprint()
    {
        if (!$this->modeSaas()) {
            return false;
        }

        if (!$this->blueprintPath) {
            return false;
        }

        try {
            $dataRaw = Yaml::parse($this->blueprintPath);
        } catch (ParseException $e) {
            print_r($e);
            exit();
        }
        return $dataRaw;
    }


    function getKeyMetaData()
    {
        $dataRaw = $this->readBlueprint();
        if (!is_array($dataRaw)) {
            return false;
        }

        $fields = $dataRaw['form']['fields'];
        $data = array();
        foreach ($fields as $field => $fdata) {

            if ($this->_checkPermission($fdata)) {
                $data[$field] = $fdata;
            }
        }

        return $data;
    }

    function checkModulePermission()
    {
        if (!$this->modeSaas()) {
            return true;
        }

        $userPermission = $this->_getUserPermission();

        if ($userPermission == 'saas_admin') {
            return true;
        }
        $dataRaw = $this->readBlueprint();
        return $this->_checkPermission($dataRaw['form']);
    }


    function getModulePermission()
    {
        $userPermission = $this->_getUserPermission();
        if ($userPermission == 'saas_admin') {
            $dataRaw = $this->readBlueprint();
            return array('permissions' => $dataRaw['form']['permissions']);
        }
        return false;
    }

    private function _checkPermission($data)
    {
        $userPermission = $this->_getUserPermission();
        return $data['permissions'][$userPermission];
    }


    function hiddenHint($data)
    {
        $hint = false;
        $userPermission = $this->_getUserPermission();
        if ($userPermission == 'saas_admin') {

            $hidden = array();
            if (!$data['permissions']['saas_user']) {
                $hidden[] = 'saas_user';
            }
            if (!$data['permissions']['saas_superuser']) {
                $hidden[] = 'saas_superuser';
            }

            if (sizeof($hidden) > 0) {
                $hint = '<span style="text-decoration: line-through; color: red">' . implode(', ', $hidden) . '</span>';
            }

        }
        return $hint;

    }

    private function _getUserPermission()
    {
// saas
//        $fdata['permissions']['saas_admin']; // sys admin mode
//        $fdata['permissions']['saas_superuser']; // superuser mode
//        $fdata['permissions']['saas_user']; // user mode
//
// on premisses
//        $fdata['permissions']['core_admin']; // admin mode
//        $fdata['permissions']['core_user']; // simple mode

        if ($this->modeSaas()) {
            if (isset($_SESSION['SAAS_ADMIN_MODE']) && $_SESSION['SAAS_ADMIN_MODE'] == true) {
                $permission = 'saas_admin';
            } else {
                $permission = 'saas_superuser';
            }
        } else {
            $permission = 'core_admin';
        }
        return $permission;
    }


    function _blueprint()
    {
        if (!$this->modeSaas()) {
//            return true;
        }
        $this->blueprintPath = $this->_getBlueprintPath();


        if (!$this->blueprintPath || file_exists($this->blueprintPath)) {
            return false;
        }

        $keys = $this->keys();

        $data = array();

        $defaultPermissions = array('saas_user' => false, 'saas_superuser' => false, 'saas_admin' => true, 'core_user' => true, 'core_admin' => true);

        foreach ($keys as $k) {
            // get type through set_function

            $field_type = 'input';
            $config_item = mh_read_config_item($k);

            if (stristr($config_item['set_function'], 'mh_cfg_select_option')) {
                $field_type = 'toggle';
            }


            $field_data[$k] = array(
                'permissions' => $defaultPermissions,
                'type' => $field_type, // input | toggle | text
                'validate' => array(
                    'type' => '', // bool | number | lower
                    'required' => '',
                    'rule' => '',
                    'min' => '',
                    'max' => ''
                ),


            ); // field permissions - show or hide field
        }

        $data['title'] = $this->code;
        $data['version'] = (float)$this->version;
        $data['generated'] = date('Y-m-d');
        $data['updated'] = '';

        $data['form']['permissions'] = $defaultPermissions; // global permission - show or hide module
        $data['form']['fields'] = $field_data;

        $yaml = Yaml::dump($data, 5);


        return file_put_contents($this->blueprintPath, $yaml);
    }


    function _getBlueprintPath()
    {
        if (!$this->module) {
            return false;
        }
        $module_directory_array = glob(MH_DIR_FS_CATALOG . MH_ROOT_PATH . '*/' . $this->module, GLOB_ONLYDIR);
        $module_directory = $module_directory_array[0];
        $path = $module_directory . '/blueprint.yaml';
        return $path;
    }


    public static function _cleanJsonComments($json_string)
    {
        // https://stackoverflow.com/questions/19509863/how-to-remove-js-comments-using-php
        $pattern = '/(?:(?:\/\*(?:[^*]|(?:\*+[^*\/]))*\*+\/)|(?:(?<!\:|\\\|\')\/\/.*))/';
        $json_string = preg_replace($pattern, '', (string)$json_string);
        $json_string = str_replace("\n", '', (string)$json_string);
        return $json_string;
    }

}