<?php
namespace Flyerimage\App\Services;

use DateTime;
use DateInterval;
use Flyerimage\App\Constants\ITables;
use Flyerimage\App\Exceptions\ServiceException;
use Flyerimage\Core\Database\QueryBuilder;
use Flyerimage\Core\Util\Helper;

/**
 * FlyerImageService service
 *
 * @author Pritom Kumar Mondal <pritomkucse@gmail.com>
 * @version
 */
class FlyerImageService extends AbstractService {
    /**
     *
     * @var WpFilterCallback $wpFilterCallback
     */
    protected $wpFilterCallback;

    /**
     * Service class constructor
     */
    public function __construct()
    {
        parent::__construct(ITables::$FLYER_IMAGES);
    }

    private function build_unique_image_filename($image_filename) {
        $image_basename = basename($image_filename);
        $image_name = pathinfo($image_basename, PATHINFO_FILENAME);
        $image_extension = pathinfo($image_basename, PATHINFO_EXTENSION);
        $unique_name = md5($image_name) . time();

        if ($image_extension !== '') {
            return $unique_name . '.' . $image_extension;
        }

        return $unique_name;
    }

    static $FLYER_TOP_BOTTOM_NOTE_MAX_LENGTH = 300;
    static $FLYER_TOP_BOTTOM_LINK_MAX_LENGTH = 2500;
    const SHAPE_TYPES = [
        'rectangle', 'ellipse', 'triangle', 'diamond', 'pentagon', 'hexagon', 'octagon',
        'star', 'heart', 'oval', 'arrow-up', 'arrow-down', 'arrow-left', 'arrow-right',
        'close-icon'
    ];

    public function create_configuration() {
        $load_templates = $this->getParamString("load_templates", "");
        if ($load_templates != "") {
            $templates = $this->load_templates();

            $configs = [];
            $configs["note_max_length"] = FlyerImageService::$FLYER_TOP_BOTTOM_NOTE_MAX_LENGTH;
            $configs["link_max_length"] = FlyerImageService::$FLYER_TOP_BOTTOM_LINK_MAX_LENGTH;
            $configs["site_url"] = FLYER_IMAGE_URL;
            $configs["base_url"] = get_site_url();

            return ["templates" => $templates, "configs" => $configs];
        }

        $template_id = $this->getParamString("template_id", "");
        if ($template_id != "") {
            global $wpdb;

            $table = self::get_table(ITables::$FLYER_TEMPLATES);

            $rows = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT template_data FROM {$table} WHERE id = %d",
                    $template_id
                )
            );

            if (count($rows) > 0) {
                $configs = [];
                $configs["note_max_length"] = FlyerImageService::$FLYER_TOP_BOTTOM_NOTE_MAX_LENGTH;
                $configs["link_max_length"] = FlyerImageService::$FLYER_TOP_BOTTOM_LINK_MAX_LENGTH;
                $configs["site_url"] = FLYER_IMAGE_URL;
                $configs["base_url"] = get_site_url();

                $form_data = json_decode($rows[0]->template_data);
                if (!is_null($form_data)) {
                    $form_data->template_id = $template_id;
                    return ["form" => $form_data, "configs" => $configs];
                }
            }
        }

        $edit_id = $this->getParamString("edit_id", "");
        $record = [];
        if (Helper::is_valid_table_id($edit_id)) {
            $entity = QueryBuilder::table($this->table_name, 'FI')
                ->select(['FI.*'])
                ->where(['FI.id' => $edit_id])
                ->get();
            if (count($entity) == 0) {
                return ["invalid" => 1];
            }

            $flyer_configs = json_decode($entity[0]->flyer_configs, true);

            $record = [
                "id" => $entity[0]->id,
                "title" => $entity[0]->title,
                "description" => $entity[0]->description,
                "start_date" => $entity[0]->start_date,
                "end_date" => $entity[0]->end_date,
            ];
            foreach ($flyer_configs as $k => $v) {
                $record[$k] = $v;
                if ($k == "pages" || $k == "categories") {
                    $record[$k] = Helper::is_valid_table_id_concat($record[$k]) ? explode(",", $record[$k]) : [];
                }
            }

            if ( isset($record["specify_start_date"]) && $record["specify_start_date"] ) {
                if (Helper::is_valid_string($record["start_time"])) {
                    $record["start_time"] = substr($record["start_time"], 1, 2).":".substr($record["start_time"], 3, 2);
                }
                else {
                    $record["start_time"] = "";
                }
            }
            else {
                $record["start_date"] = "";
                $record["start_time"] = "";
            }

            if ( isset($record["specify_end_date"]) && $record["specify_end_date"] ) {
                if (Helper::is_valid_string($record["end_time"])) {
                    $record["end_time"] = substr($record["end_time"], 1, 2).":".substr($record["end_time"], 3, 2);
                }
                else {
                    $record["end_time"] = "";
                }
            }
            else {
                $record["end_date"] = "";
                $record["end_time"] = "";
            }

            // Build page_list for frontend display from the saved page IDs
            $record["page_list"] = [];
            if ( isset($record["pages"]) && count($record["pages"]) > 0 ) {
                // Add Home Page if it was selected
                if (in_array("-1", $record["pages"])) {
                    $record["page_list"][] = [
                        "id" => "-1", "title" => __("Home Page", 'flyerimage'),
                        "type" => "page", "post_name" => "", "selected" => true
                    ];
                }
                
                // Filter out "-1" and only query real post IDs from WordPress
                $post_ids = array_filter($record["pages"], function($id) { return $id !== "-1"; });
                
                if (count($post_ids) > 0) {
                    $args = array(
                        'post__in'          => $post_ids,
                        'post_type'         => array( 'page', 'post' ),
                        'orderby'           => 'ID',
                        'order'             => 'ASC',
                        'offset'            => 0,
                        'posts_per_page'    => -1,
                        // array('publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash')
                        'post_status'       => array('publish')
                    );
                    // https://developer.wordpress.org/reference/classes/wp_query/
                    $the_query = new \WP_Query( $args );
                    foreach($the_query->posts as $post) {
                        $record["page_list"][] = [
                            "id" => $post->ID, "title" => $post->post_title,
                            "type" => $post->post_type, "post_name" => $post->post_name, "selected" => true
                        ];
                    }
                }
            }
            
            // Build category_list for frontend display from the saved category IDs
            $record["category_list"] = [];
            if ( isset($record["categories"]) && count($record["categories"]) > 0 ) {
                foreach ( get_categories(['include' => $record["categories"]]) as $item ) {
                    $record["category_list"][] = ["id" => $item->cat_ID, "title" => $item->name, "selected" => true];
                }
            }

            $record["screens"] = [];
            $screen_record = QueryBuilder::table(ITables::$FLYER_IMAGES_SCREENS, 'FI_IMAGES')
                ->select(['FI_IMAGES.*'])
                ->where(['FI_IMAGES.flyer_image_id' => $entity[0]->id])
                ->get();
            foreach ($screen_record as $screen) {
                $screen->elements = json_decode($screen->elements, true);
                $screen->background_image = json_decode($screen->background_image, true);
                $record["screens"][] = $screen;
            }
            
            // If no screens found, provide default screen structure to prevent frontend errors
            if (empty($record["screens"])) {
                $default_screen = (object)[
                    'screen_name' => 'desktop',
                    'screen_width' => '900',
                    'screen_height' => '600',
                    'elements' => [],
                    'background_image' => []
                ];
                $record["screens"][] = $default_screen;
            }
        }
        
        // Add images from flyer image history
        $record["images"] = [];
        if (Helper::is_valid_table_id($edit_id)) {
            $image_records = QueryBuilder::table(ITables::$FLYER_IMAGES_HISTORY)
                ->select(['image_path'])
                ->where(['used_by' => $edit_id])
                ->order_by('created_at DESC')
                ->get();
            
            foreach ($image_records as $img_record) {
                $upload_dir = wp_upload_dir();
                $record["images"][] = [
                    'image_path' => $img_record->image_path,
                    'image_url' => $upload_dir['baseurl'] . "/flyerimage/" . $img_record->image_path
                ];
            }
        }
        
        $configs = [];
        $configs["note_max_length"] = FlyerImageService::$FLYER_TOP_BOTTOM_NOTE_MAX_LENGTH;
        $configs["link_max_length"] = FlyerImageService::$FLYER_TOP_BOTTOM_LINK_MAX_LENGTH;
        $configs["site_url"] = FLYER_IMAGE_URL;
        $configs["base_url"] = get_site_url();

        return ["form" => $record, "configs" => $configs];
    }

    public function list_flyers() {
        $page = $this->getParamValue("page_number", 1);

        $limit = 10;

        $pagination = QueryBuilder::table($this->table_name, 'FI')
            ->select(['FI.*'])
            ->order_by('FI.created_at desc')
            ->page($page)
            ->limit($limit);

        $title = $this->getParamString("title");
        if (Helper::is_valid_string($title)) {
            $pagination->whereRaw("FI.title LIKE %s", ["%$title%"]);
        }

        $status = mb_strtolower($this->getParamString("status", ""));
        if ($status == "active" || $status == "inactive") {
            $pagination->where(["status" => $status]);
        }

        $pagination = $pagination->pagination();

        $date_format = get_option('date_format');
        $time_format = get_option('time_format');
        foreach ($pagination["list"] as &$item) {
            if (!is_null($item->start_date)) {
                $item->start_date = gmdate($date_format, strtotime($item->start_date));
                if (!is_null($item->start_time)) {
                    $item->start_time = gmdate($time_format,  strtotime("2020-01-01 ".substr($item->start_time, 1, 2).":".substr($item->start_time, 3, 2)) );
                }
                else {
                    $item->start_time= "";
                }
            }
            else {
                $item->start_date= "";
                $item->start_time= "";
            }

            if (!is_null($item->end_date)) {
                $item->end_date = gmdate($date_format, strtotime($item->end_date));
                if (!is_null($item->end_time)) {
                    $item->end_time = gmdate($time_format,  strtotime("2020-01-01 ".substr($item->end_time, 1, 2).":".substr($item->end_time, 3, 2)) );
                }
                else {
                    $item->end_time= "";
                }
            }
            else {
                $item->end_date= "";
                $item->end_time= "";
            }

            $item->flyer_configs = Helper::parse_json_string($item->flyer_configs, []);

            $item->page_visibility = null;
            $item->v3_meta = null;

            // v3 campaigns: derive visibility + extra meta from v3_canvas JSON
            if (($item->campaign_version ?? '') === '3') {
                $cv = $item->flyer_configs['v3_canvas'] ?? [];
                $scope = $cv['scope'] ?? 'all';
                if ($scope === 'home') {
                    $item->page_visibility = __("Homepage only", 'flyerimage');
                } elseif ($scope === 'specific') {
                    $rules = $cv['urlRules'] ?? [];
                    $count = count($rules);
                    $item->page_visibility = $count > 0
                        ? sprintf(_n('%d specific page', '%d specific pages', $count, 'flyerimage'), $count)
                        : __("No pages selected", 'flyerimage');
                } else {
                    $item->page_visibility = __("All pages", 'flyerimage');
                }

                // Trigger
                $trigger = $cv['trigger'] ?? '';
                $tv      = $cv['triggerValue'] ?? 0;
                $trigger_labels = [
                    'time'   => "After {$tv}s on page",
                    'timer'  => "After {$tv}s on page",
                    'scroll' => "Scroll {$tv}%",
                    'exit'   => 'Exit intent',
                    'idle'   => "Idle {$tv}s",
                    'click'  => 'Element click',
                    'load'   => 'On page load',
                ];
                $item->v3_meta = [
                    'trigger'  => $trigger_labels[$trigger] ?? ucfirst($trigger),
                    'visitor'  => $cv['visitor'] ?? 'all',
                    'devices'  => array_keys(array_filter($cv['devicesTarget'] ?? ['desktop' => true])),
                    'freq'     => $cv['freq'] ?? 'once',
                ];
            } elseif (isset($item->flyer_configs["show_for_all_pages"]) && $item->flyer_configs["show_for_all_pages"] == 1) {
                $item->page_visibility = __("For all pages", 'flyerimage');
            } else {
                if (isset($item->flyer_configs['pages']) && $item->flyer_configs['pages'] === '-1') {
                    $item->page_visibility = __("Homepage only", 'flyerimage');
                } else {
                    $item->page_count = Helper::is_valid_table_id_concat($item->flyer_configs['pages']) ? count(explode(",", $item->flyer_configs['pages'])) : 0;
                    $item->category_count = Helper::is_valid_table_id_concat($item->flyer_configs['categories']) ? count(explode(",", $item->flyer_configs['categories'])) : 0;
                    if ($item->page_count == 0 && $item->category_count == 0) {
                        $item->page_visibility = __("Not listed for any pages", 'flyerimage');
                    }
                }
            }
        }

        //$pagination["date_format"] = get_option('date_format');
        //$pagination["date_format4"] = date(get_option('date_format'), strtotime("2024-01-02"));
        //$pagination["time_format4"] = date(get_option('time_format'), strtotime("16:20"));

        return $pagination;
    }

    public function save_flyer() {
        $title = $this->getParamString("title", "", 70);
        if (!Helper::is_valid_string($title)) {
            throw new ServiceException(esc_html__("Title required", 'flyerimage'));
        }

        $edit_id = Helper::is_valid_table_id($this->getParamString("id", "", 20)) ? $this->getParamString("id", "", 20) : null;

        $template_id = Helper::is_valid_table_id($this->getParamString("template_id", "", 20)) ? $this->getParamString("template_id", "", 20) : null;
        $duplicate_id = !$template_id && Helper::is_valid_table_id($this->getParamString("duplicate_id", "", 20)) ? $this->getParamString("duplicate_id", "", 20) : null;

        $duplicate = QueryBuilder::table($this->table_name, 'FI')
            ->select(['FI.id'])
            ->where(['title' => $title]);
        if ($edit_id) {
            $duplicate->where(["FI.id", "<>", $edit_id]);
        }
        if (count($duplicate->get()) > 0) {
            throw new ServiceException(esc_html__('Duplicate title', 'flyerimage'));
        }

        $configs = [];

        $configs["specify_start_date"] = $this->getParamValue("specify_start_date", "") == true ? 1 : 0;
        if ($configs["specify_start_date"] == 1) {
            $start_date = Helper::get_valid_date_string($this->getParamString("start_date", "", 10));
            if (is_null($start_date)) {
                throw new ServiceException(esc_html__('Invalid start date', 'flyerimage'));
            }
            $configs["start_date"] = $start_date->format("Y-m-d");

            $start_time = Helper::get_valid_time_string($this->getParamString("start_time", "", 5));
            if (is_null($start_time)) {
                $configs["start_time"] = null;
            }
            else {
                $configs["start_time"] = "1".str_replace(":", "", $start_time);
            }
        }
        else {
            $configs["start_date"] = null;
            $configs["start_time"] = null;
        }

        $configs["specify_end_date"] = $this->getParamValue("specify_end_date", "") == true ? 1 : 0;
        if ($configs["specify_end_date"] == 1) {
            $end_date = Helper::get_valid_date_string($this->getParamString("end_date", "", 10));
            if (is_null($end_date)) {
                throw new ServiceException(esc_html__('Invalid end date', 'flyerimage'));
            }
            $configs["end_date"] = $end_date->format("Y-m-d");

            $end_time = Helper::get_valid_time_string($this->getParamString("end_time", "", 5));
            if (is_null($end_time)) {
                $configs["end_time"] = null;
            }
            else {
                $configs["end_time"] = intval("1".str_replace(":", "", $end_time));
            }
        }
        else {
            $configs["end_date"] = null;
            $configs["end_time"] = null;
        }

        $configs["close_automatically"] = $this->getParamValue("close_automatically", "") == true ? 1 : 0;
        if ($configs["close_automatically"] == 1) {
            $configs["close_flyer_after_seconds"] = intval($this->getParamString("close_flyer_after_seconds", ""));
            if ($configs["close_flyer_after_seconds"] <= 0 || $configs["close_flyer_after_seconds"] > 180) {
                $configs["close_flyer_after_seconds"] = 30;
            }
            $configs["show_close_icon"] = $this->getParamValue("show_close_icon", "") == true ? 1 : 0;
        }
        else {
            $configs["close_flyer_after_seconds"] = "0";
            $configs["show_close_icon"] = 1;
        }
        $configs["show_for_all_pages"] = $this->getParamValue("show_for_all_pages", "") == true ? 1 : 0;

        $configs["pages"] = $configs["show_for_all_pages"] == 1 ? "" : $this->getParamValue("pages", "");
        $configs["pages"] = Helper::is_valid_table_id_concat($configs["pages"]) ? $configs["pages"] : "";

        $configs["categories"] = $configs["show_for_all_pages"] == 1 ? "" : $this->getParamValue("categories", "");
        $configs["categories"] = Helper::is_valid_table_id_concat($configs["categories"]) ? $configs["categories"] : "";

        // v3 canvas — store the full designer state for round-trip editing
        $v3_canvas_raw = $this->getParamValue("v3_canvas", null);
        if (!is_null($v3_canvas_raw)) {
            $configs["v3_canvas"]        = $v3_canvas_raw;
            $configs["campaign_version"] = "3";

            // Store v3 rule fields flat so they are directly accessible without parsing v3_canvas
            $allowed_triggers = ["exit", "time", "scroll", "click", "idle"];
            $trigger_type = isset($v3_canvas_raw["trigger"]) ? $v3_canvas_raw["trigger"] : "exit";
            $configs["v3_trigger_type"]   = in_array($trigger_type, $allowed_triggers) ? $trigger_type : "exit";
            $configs["v3_trigger_value"]  = isset($v3_canvas_raw["triggerValue"]) ? intval($v3_canvas_raw["triggerValue"]) : 0;

            $allowed_scopes = ["all", "home", "specific"];
            $scope = isset($v3_canvas_raw["scope"]) ? $v3_canvas_raw["scope"] : "all";
            $configs["v3_scope"]          = in_array($scope, $allowed_scopes) ? $scope : "all";
            $configs["v3_url_rules"]      = isset($v3_canvas_raw["urlRules"]) ? $v3_canvas_raw["urlRules"] : [];

            $allowed_visitors = ["new", "returning", "all"];
            $visitor = isset($v3_canvas_raw["visitor"]) ? $v3_canvas_raw["visitor"] : "all";
            $configs["v3_visitor"]        = in_array($visitor, $allowed_visitors) ? $visitor : "all";
            $configs["v3_devices_target"] = isset($v3_canvas_raw["devicesTarget"]) ? $v3_canvas_raw["devicesTarget"] : [];

            $allowed_freq = ["session", "visitor", "days"];
            $freq = isset($v3_canvas_raw["freq"]) ? $v3_canvas_raw["freq"] : "session";
            $configs["v3_freq"]              = in_array($freq, $allowed_freq) ? $freq : "session";
            $configs["v3_stop_on_convert"]   = !empty($v3_canvas_raw["stopOnConvert"]) ? 1 : 0;
        }

        // v3 live toggle → status column
        $v3_live = $this->getParamValue("v3_live", null);
        $v3_status = null;
        if (!is_null($v3_live)) {
            $v3_status = $v3_live ? "active" : "inactive";
        }

        $insert = [];
        $insert["title"] = $title;
        $insert["description"] = $this->getParamString("description", "", 400);
        $insert["start_date"] = $configs["start_date"];
        $insert["start_time"] = $configs["start_time"];
        $insert["end_date"] = $configs["end_date"];
        $insert["end_time"] = $configs["end_time"];
        $insert["show_for_all_pages"] = $configs["show_for_all_pages"];
        if (!is_null($v3_status)) {
            $insert["status"] = $v3_status;
        }
        // Populate dedicated v3 columns only if they exist (added by migration 0007)
        if (isset($configs["campaign_version"]) && $configs["campaign_version"] === "3") {
            global $wpdb;
            $tbl = $this->get_table($this->table_name);
            $col_check = $wpdb->get_results( $wpdb->prepare( "SHOW COLUMNS FROM `{$tbl}` LIKE %s", "campaign_version" ) );
            if ( count( $col_check ) > 0 ) {
                $insert["campaign_version"] = "3";
                $insert["trigger_type"]     = $configs["v3_trigger_type"];
            }
        }
        $insert["flyer_configs"] = json_encode($configs);
        if ($edit_id) {
            $insert["updated_by"] = get_current_user_id();
            $insert["updated_at"] = current_time('mysql');
        }
        else {
            $insert["created_by"] = $insert["updated_by"] = get_current_user_id();
            $insert["created_at"] = $insert["updated_at"] = current_time('mysql');
        }

        $screens = [];
        $params = $this->params;
        $valid_image_id_list = [];
        $output = [];
        try {
            foreach ($this->getParamValue("screens", []) as $screen) {
                $this->params = $screen;

                $screen_data = [];

                $screen_data["id"] = $this->getParamString("id", "");

                $screen_data["screen_name"] = $this->getParamString("screen_name", "");
                if (!Helper::is_valid_string($screen_data["screen_name"])) {
                    throw new ServiceException(esc_html__('Invalid screen type', 'flyerimage'));
                }

                $screen_data["screen_height"] = $this->getParamString("screen_height", "");
                if (!Helper::is_positive_integer($screen_data["screen_height"])) {
                    throw new ServiceException(esc_html__('Invalid screen height', 'flyerimage'));
                }

                $screen_data["screen_width"] = $this->getParamString("screen_width", "");
                if (!Helper::is_positive_integer($screen_data["screen_width"])) {
                    throw new ServiceException(esc_html__('Invalid screen width', 'flyerimage'));
                }

                $upload_dir = wp_upload_dir();
                $flyerimage_upload_dir = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'flyerimage';
                if (!is_dir($flyerimage_upload_dir)) {
                    if (!wp_mkdir_p($flyerimage_upload_dir)) {
                        throw new ServiceException(esc_html__('Unable to create upload directory', 'flyerimage'));
                    }
                }

                $screen_data["elements"] = [];
                foreach ($this->getParamValue("elements", []) as $index => $element) {
                    $other_params = $this->params;
                    try {
                        $this->params = $element;

                        $element_data = [];

                        $element_data["width"] = $this->getParamString("width", "");
                        if (!Helper::is_positive_integer($element_data["width"])) {
                            //error_log( "Invalid component width for screen=".$screen_data["screen_name"].", index=$index" );
                            throw new ServiceException(esc_html__('Invalid component width', 'flyerimage'));
                        }

                        $element_data["height"] = $this->getParamString("height", "");
                        if (!Helper::is_positive_integer($element_data["height"])) {
                            throw new ServiceException(esc_html__('Invalid component height', 'flyerimage'));
                        }

                        $element_data["x"] = $this->getParamString("x", "");
                        if (!Helper::is_positive_integer($element_data["x"])) {
                            //error_log( "Invalid component x for screen=".$screen_data["screen_name"].", index=$index" );
                            throw new ServiceException(esc_html__('Invalid component width', 'flyerimage'));
                        }

                        $element_data["y"] = $this->getParamString("y", "");
                        if (!Helper::is_positive_integer($element_data["y"])) {
                            //error_log( "Invalid component y for screen=".$screen_data["screen_name"].", index=$index" );
                            throw new ServiceException(esc_html__('Invalid component height', 'flyerimage'));
                        }

                        $element_data["type"] = $this->getParamString("type", "");
                        if (!Helper::is_valid_string($element_data["type"])) {
                            throw new ServiceException(esc_html__('Invalid component type', 'flyerimage'));
                        }

                        $element_data["transparent_background"] = $this->getParamValue("transparent_background", false);

                        $element_data["zIndex"] = $this->getParamString("zIndex", "1");
                        if (!Helper::is_positive_integer($element_data["zIndex"])) {
                            $element_data["zIndex"] = "1";
                        }

                        $element_data["rotation"] = $this->getParamString("rotation", "0");
                        if (!Helper::is_valid_integer($element_data["rotation"])) {
                            $element_data["rotation"] = "0";
                        }
                        $element_data["rotation"] = intval($element_data["rotation"]);
                        $element_data["rotation"] = $element_data["rotation"] >= -180 && $element_data["rotation"] <= 180 ? $element_data["rotation"] : 0;

                        $element_data["shadowEnabled"] = $this->getParamString("shadowEnabled", "false") == "1";
                        $element_data["shadowType"] = $this->getParamString("shadowType", "outer");
                        $element_data["shadowColor"] = $this->getParamString("shadowColor", "black");
                        $element_data["shadowLeft"] = $this->getParamString("shadowLeft", "false") == "1";
                        $element_data["shadowRight"] = $this->getParamString("shadowRight", "false") == "1";
                        $element_data["shadowTop"] = $this->getParamString("shadowTop", "false") == "1";
                        $element_data["shadowBottom"] = $this->getParamString("shadowBottom", "false") == "1";

                        $element_data["shadowWidth"] = $this->getParamString("shadowWidth", "0");
                        if (!Helper::is_valid_integer($element_data["shadowWidth"])) {
                            $element_data["shadowWidth"] = "0";
                        }
                        $element_data["shadowWidth"] = intval($element_data["shadowWidth"]);
                        $element_data["shadowWidth"] = max(min($element_data["shadowWidth"], 40), 0);

                        $element_data["shadowDepth"] = $this->getParamString("shadowDepth", "1");
                        if (!Helper::is_valid_integer($element_data["shadowDepth"])) {
                            $element_data["shadowDepth"] = "1";
                        }
                        $element_data["shadowDepth"] = intval($element_data["shadowDepth"]);
                        $element_data["shadowDepth"] = max(min($element_data["shadowDepth"], 40), 1);

                        if ($element_data["type"] == "label") {
                            $element_data["label_text"] = $this->getParamString("label_text", "");
                            $element_data["backgroundColor"] = $this->getParamString("backgroundColor", "#ffffff");
                            $element_data["transparent_background"] = $this->getParamValue("transparent_background", false);
                            $element_data["show_under_line_for_hyperlink"] = $this->getParamValue("show_under_line_for_hyperlink", false);
                            $element_data["lineHeight"] = $this->getParamString("lineHeight", "10");
                            $element_data["lineHeight"] = $this->validateIntegerValue($element_data["lineHeight"], 5, 30);
                        }
                        else if ($element_data["type"] == "image") {
                            $element_data["image_id"] = $this->getParamString("image_id", "");

                            $element_data["image_path"] = $this->getParamString("image_path", "");
                            if (!Helper::is_valid_string($element_data["image_path"])) {
                                throw new ServiceException(esc_html__('Invalid image path', 'flyerimage'));
                            }

                            if ($template_id  && !Helper::is_valid_table_id($element_data["image_id"])) {
                                // Copy image to upload directory
                                $image_filename = $element_data["image_path"];
                                $from_path = FLYER_IMAGE_PUBLIC_PATH . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, ["static", "images", "template{$template_id}", $image_filename]);

                                if (file_exists($from_path)) {
                                    $image_filename = $this->build_unique_image_filename($image_filename);
                                    $to_path = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $image_filename;

                                    if (!copy($from_path, $to_path)) {
                                        throw new ServiceException(esc_html__('Failed to copy image to upload directory', 'flyerimage'));
                                    }

                                    $params = ["image_path" => $image_filename, "created_at" => gmdate("Y-m-d")];
                                    $id = QueryBuilder::table(ITables::$FLYER_IMAGES_HISTORY)->create($params);

                                    $element_data["image_id"] = $id;
                                    $element_data["image_path"] = $image_filename;
                                }
                                else {
                                    throw new ServiceException(esc_html__('Image file not found in template directory', 'flyerimage'));
                                }
                            }
                            else if ($duplicate_id  && !Helper::is_valid_table_id($element_data["image_id"])) {
                                // Copy image to upload directory
                                $image_filename = $element_data["image_path"];
                                $from_path = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $image_filename;

                                if (file_exists($from_path)) {
                                    $image_filename = $this->build_unique_image_filename($image_filename);
                                    $to_path = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $image_filename;

                                    if (!copy($from_path, $to_path)) {
                                        throw new ServiceException(esc_html__('Failed to copy image to upload directory', 'flyerimage'));
                                    }

                                    $params = ["image_path" => $image_filename, "created_at" => gmdate("Y-m-d")];
                                    $id = QueryBuilder::table(ITables::$FLYER_IMAGES_HISTORY)->create($params);

                                    $element_data["image_id"] = $id;
                                    $element_data["image_path"] = $image_filename;
                                }
                                else {
                                    throw new ServiceException(esc_html__('Image file not found in upload directory', 'flyerimage'));
                                }
                            }
                            if (!Helper::is_valid_table_id($element_data["image_id"])) {
                                throw new ServiceException(esc_html__('Invalid image ID', 'flyerimage'));
                            }
                            $valid_image_id_list[] = $element_data["image_id"];

                            $element_data["image_width"] = $this->getParamString("image_width", "");
                            if (!Helper::is_positive_integer($element_data["image_width"])) {
                                throw new ServiceException(esc_html__('Invalid image width', 'flyerimage'));
                            }

                            $element_data["image_height"] = $this->getParamString("image_height", "");
                            if (!Helper::is_positive_integer($element_data["image_height"])) {
                                throw new ServiceException(esc_html__('Invalid image height', 'flyerimage'));
                            }

                            $element_data["opacity"] = $this->getParamString("opacity", "100");
                            $element_data["image_shape"] = $this->getParamString("image_shape", "");
                            $element_data["hyperlink"] = $this->getParamString("hyperlink", "");
                            // Use uploads directory URL for images
                            $upload_dir = wp_upload_dir();
                            $element_data["image_url"] = $upload_dir['baseurl'] . "/flyerimage/" . $element_data["image_path"];
                        }
                        else if ($element_data["type"] == "close-icon") {
                            $element_data["backgroundColor"] = $this->getParamString("backgroundColor", "#e5152f");
                            $element_data["iconColor"] = $this->getParamString("iconColor", "#ffffff");
                            $element_data["iconType"] = $this->getParamString("iconType", "default");
                        }
                        else if (in_array($element_data["type"], self::SHAPE_TYPES, true)) {
                            $element_data["backgroundColor"] = $this->getParamString("backgroundColor", "#ffffff");
                            $element_data["borderColor"] = $this->getParamString("borderColor", "#000000");
                            $element_data["borderWidth"] = $this->getParamString("borderWidth", "0");
                            $element_data["borderRadius"] = $this->getParamString("borderRadius", "0");
                            $element_data["opacity"] = $this->validateIntegerValue($this->getParamString("opacity", "100"), 0, 100);
                        }
                        else {
                            throw new ServiceException(esc_html__('Invalid component type', 'flyerimage'));
                        }

                        $screen_data["elements"][] = $element_data;
                    }
                    finally {
                        $this->params = $other_params;
                    }
                }

                $screen_data["background_image"] = [];
                $other_params = $this->params;
                try {
                    $background_image_object = ["set_background_image_for_the_flyer" => false];

                    $background_image = $this->getParamValue("background_image");
                    if (!is_null($background_image) && is_array($background_image)) {
                        $this->params = $background_image;

                        $background_image_object["background_transparent"] = $this->getParamString("background_transparent", "false");
                        $background_image_object["background_transparent"] = strval($background_image_object["background_transparent"]) == "true";

                        $background_image_object["background_color"] = $this->getParamString("background_color", "#ffffff");

                        $background_image_object["shadowEnabled"] = $this->getParamString("shadowEnabled", "false") == "1";
                        $background_image_object["shadowType"] = $this->getParamString("shadowType", "outer");
                        $background_image_object["shadowColor"] = $this->getParamString("shadowColor", "black");
                        $background_image_object["shadowLeft"] = $this->getParamString("shadowLeft", "false") == "1";
                        $background_image_object["shadowRight"] = $this->getParamString("shadowRight", "false") == "1";
                        $background_image_object["shadowTop"] = $this->getParamString("shadowTop", "false") == "1";
                        $background_image_object["shadowBottom"] = $this->getParamString("shadowBottom", "false") == "1";

                        $background_image_object["shadowWidth"] = $this->getParamString("shadowWidth", "0");
                        if (!Helper::is_valid_integer($background_image_object["shadowWidth"])) {
                            $background_image_object["shadowWidth"] = "0";
                        }
                        $background_image_object["shadowWidth"] = intval($background_image_object["shadowWidth"]);
                        $background_image_object["shadowWidth"] = max(min($background_image_object["shadowWidth"], 40), 0);

                        $background_image_object["shadowDepth"] = $this->getParamString("shadowDepth", "1");
                        if (!Helper::is_valid_integer($background_image_object["shadowDepth"])) {
                            $background_image_object["shadowDepth"] = "1";
                        }
                        $background_image_object["shadowDepth"] = intval($background_image_object["shadowDepth"]);
                        $background_image_object["shadowDepth"] = max(min($background_image_object["shadowDepth"], 40), 1);

                        $set_background_image_for_the_flyer = $this->getParamValue("set_background_image_for_the_flyer", false);
                        if ($set_background_image_for_the_flyer && !$background_image_object["background_transparent"]) {
                            $background_image_object["set_background_image_for_the_flyer"] = $set_background_image_for_the_flyer;

                            $background_image_object["image_id"] = $this->getParamString("image_id", "");

                            $background_image_object["image_path"] = $this->getParamString("image_path", "");
                            if (!Helper::is_valid_string($background_image_object["image_path"])) {
                                throw new ServiceException(esc_html__('Invalid image path', 'flyerimage'));
                            }

                            if ($template_id  && !Helper::is_valid_table_id($background_image_object["image_id"])) {
                                // Copy image to upload directory
                                $image_filename = $background_image_object["image_path"];
                                $from_path = FLYER_IMAGE_PUBLIC_PATH . DIRECTORY_SEPARATOR . implode(DIRECTORY_SEPARATOR, ["static", "images", "template{$template_id}", $image_filename]);

                                if (file_exists($from_path)) {
                                    $image_filename = $this->build_unique_image_filename($image_filename);
                                    $to_path = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $image_filename;

                                    if (!copy($from_path, $to_path)) {
                                        throw new ServiceException(esc_html__('Failed to copy image to upload directory', 'flyerimage'));
                                    }

                                    $params = ["image_path" => $image_filename, "created_at" => gmdate("Y-m-d")];
                                    $id = QueryBuilder::table(ITables::$FLYER_IMAGES_HISTORY)->create($params);

                                    $background_image_object["image_id"] = $id;
                                    $background_image_object["image_path"] = $image_filename;
                                }
                                else {
                                    throw new ServiceException(esc_html__('Image file not found in template directory', 'flyerimage'));
                                }
                            }
                            else if ($duplicate_id && !Helper::is_valid_table_id($background_image_object["image_id"])) {
                                // Copy image to upload directory
                                $image_filename = $background_image_object["image_path"];
                                $from_path = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $image_filename;

                                if (file_exists($from_path)) {
                                    $image_filename = $this->build_unique_image_filename($image_filename);
                                    $to_path = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $image_filename;

                                    if (!copy($from_path, $to_path)) {
                                        throw new ServiceException(esc_html__('Failed to copy image to upload directory', 'flyerimage'));
                                    }

                                    $params = ["image_path" => $image_filename, "created_at" => gmdate("Y-m-d")];
                                    $id = QueryBuilder::table(ITables::$FLYER_IMAGES_HISTORY)->create($params);

                                    $background_image_object["image_id"] = $id;
                                    $background_image_object["image_path"] = $image_filename;
                                }
                                else {
                                    throw new ServiceException(esc_html__('Image file not found in upload directory', 'flyerimage'));
                                }
                            }

                            if (!Helper::is_valid_table_id($background_image_object["image_id"])) {
                                throw new ServiceException(esc_html__('Invalid image ID', 'flyerimage'));
                            }
                            $valid_image_id_list[] = $background_image_object["image_id"];

                            $background_image_object["hyperlink"] = $this->getParamString("hyperlink", "");
                            $background_image_object["opacity"] = intval($this->getParamString("opacity", "100"));
                            $background_image_object["borderRadius"] = intval($this->getParamString("borderRadius", "0"));
                            // Use uploads directory URL for images
                            $background_image_object["image_url"] = $upload_dir['baseurl'] . "/flyerimage/" . $background_image_object["image_path"];
                        }
                        else {
                            $background_image_object["set_background_image_for_the_flyer"] = false;
                        }
                    }
                    $screen_data["background_image"] = $background_image_object;
                }
                finally {
                    $this->params = $other_params;
                }

                $screens[] = $screen_data;
            }
        }
        finally {
            $this->params = $params;
        }

        try {
            global $wpdb;
            $wpdb->query('START TRANSACTION');

            if ($edit_id) {
                $insert["id"] = $id = $edit_id;
                if (!QueryBuilder::table($this->table_name)->save($insert)) {
                    throw new ServiceException(esc_html__('Failed to edit Flyer', 'flyerimage'));
                }
                $table22 = self::get_table(ITables::$FLYER_IMAGES_HISTORY);
                $wpdb->query(
                    $wpdb->prepare(
                        "UPDATE {$table22} SET used_by = used_by * -1 WHERE used_by = %d",
                        $edit_id
                    )
                );
                QueryBuilder::table(ITables::$FLYER_IMAGES_PAGES, 'FI')->where(['FI.flyer_image_id' => $edit_id])->delete("FI");
            }
            else {
                $id = QueryBuilder::table($this->table_name)->create($insert);
                if (!$id) {
                    throw new ServiceException(esc_html__('Failed to create Flyer', 'flyerimage'));
                }
            }
            if ( count ( $valid_image_id_list ) > 0 ) {
                QueryBuilder::table(ITables::$FLYER_IMAGES_HISTORY)
                    ->where(['id', 'IN', $valid_image_id_list])->update([
                        "used_by" => $id
                    ]);
            }

            $valid_screen_id = [0];
            foreach ($screens as $screen) {
                $screen["flyer_image_id"] = $id;
                $screen["elements"] = json_encode($screen["elements"]);
                $screen["background_image"] = json_encode($screen["background_image"]);

                if (Helper::is_valid_table_id($screen["id"])) {
                    QueryBuilder::table(ITables::$FLYER_IMAGES_SCREENS)
                        ->where(['id', '=', $screen["id"]])
                        ->where(['flyer_image_id', '=', $id])
                        ->update($screen);
                    $valid_screen_id[] = $screen["id"];
                }
                else {
                    unset($screen["id"]);
                    $insert_id = QueryBuilder::table(ITables::$FLYER_IMAGES_SCREENS)->create($screen);
                    if (!$insert_id) {
                        if ($edit_id) {
                            throw new ServiceException(esc_html__('Failed to edit Flyer', 'flyerimage'));
                        }
                        throw new ServiceException(esc_html__('Failed to create Flyer', 'flyerimage'));
                    }
                    $valid_screen_id[] = $insert_id;
                }
            }
            $valid_screen_id = implode(",", $valid_screen_id);
            QueryBuilder::table(ITables::$FLYER_IMAGES_SCREENS, 'FI')
                ->where(['FI.flyer_image_id' => $edit_id])
                ->whereRaw("FI.id NOT IN ($valid_screen_id)", [])
                ->delete("FI");

            if (Helper::is_valid_table_id_concat($configs["pages"])) {
                foreach (explode(",", $configs["pages"]) as $value) {
                    $params730 = [
                        "flyer_image_id" => $id,
                        "type" => "page",
                        "value" => $value
                    ];
                    if (!QueryBuilder::table(ITables::$FLYER_IMAGES_PAGES)->create($params730)) {
                        //error_log( 'Insert into [FLYER_IMAGES_PAGES] failed for page' );
                        //error_log( json_encode($params730) );
                        if ($edit_id) {
                            throw new ServiceException(esc_html__('Failed to edit Flyer', 'flyerimage'));
                        }
                        throw new ServiceException(esc_html__('Failed to create Flyer', 'flyerimage'));
                    }
                }
            }

            if (Helper::is_valid_table_id_concat($configs["categories"])) {
                foreach (explode(",", $configs["categories"]) as $value) {
                    $params730 = [
                        "flyer_image_id" => $id,
                        "type" => "category",
                        "value" => $value
                    ];
                    if (!QueryBuilder::table(ITables::$FLYER_IMAGES_PAGES)->create($params730)) {
                        //error_log( 'Insert into [FLYER_IMAGES_PAGES] failed for category' );
                        //error_log( json_encode($params730) );
                        if ($edit_id) {
                            throw new ServiceException(esc_html__('Failed to edit Flyer', 'flyerimage'));
                        }
                        throw new ServiceException(esc_html__('Failed to create Flyer', 'flyerimage'));
                    }
                }
            }

            $wpdb->query('COMMIT');

            if ($edit_id) {
                return ["id" => $edit_id, "message" => __("Flyer has been edited successfully", 'flyerimage'), "output" => $output];
            }
            return ["id" => $id, "message" => __("Flyer has been created successfully", 'flyerimage'), "output" => $output];
        }
        catch (\Throwable $throwable) {
            $wpdb->query('ROLLBACK');
            throw $throwable;
        }
    }

    public function save_flyer_image_copy() {
        if ( ! function_exists( 'request_filesystem_credentials' ) ) {
            require_once ABSPATH . 'wp-admin/includes/file.php';
        }
        global $wp_filesystem;
        if ( false === ( $creds = request_filesystem_credentials( site_url() ) ) ) {
            throw new ServiceException(esc_html("Could not access filesystem credentials.", 'flyerimage'));
        }

        if ( ! WP_Filesystem( $creds ) ) {
            throw new ServiceException(esc_html("Filesystem API could not be initialized.", 'flyerimage'));
        }

        // Use WordPress uploads directory instead of plugin directory
        $upload_dir = wp_upload_dir();
        $flyerimage_upload_dir = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'flyerimage';

        // Create flyerimage directory in uploads if it doesn't exist
        if (!file_exists($flyerimage_upload_dir)) {
            wp_mkdir_p($flyerimage_upload_dir);
        }

        $delete_expired_image_copy = $this->getParamValue("delete_expired_image_copy");
        if (Helper::is_valid_table_id($delete_expired_image_copy)) {
            global $wpdb;

            $table29 = self::get_table(ITables::$FLYER_IMAGES_HISTORY);

            $rows = $wpdb->get_results("SELECT * FROM {$table29} WHERE used_by=-{$delete_expired_image_copy}");
            foreach ($rows as $row) {
                $output_file = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $row->image_path;
                if (file_exists($output_file)) {
                    unlink($output_file);
                }
                $wpdb->query(
                    $wpdb->prepare(
                        "DELETE FROM {$table29} WHERE id = %d",
                        $row->id
                    )
                );
            }

            $date = new DateTime(gmdate("Y-m-d"));
            $date->sub(new DateInterval("P3D"));
            $from_date1 = $date->format("Y-m-d");
            $rows = $wpdb->get_results(
                $wpdb->prepare(
                    "SELECT * FROM {$table29} WHERE used_by = 0 AND created_at <= %s",
                    $from_date1
                )
            );
            foreach ($rows as $row) {
                $output_file = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $row->image_path;
                if (file_exists($output_file)) {
                    unlink($output_file);
                }
                $wpdb->query(
                    $wpdb->prepare(
                        "DELETE FROM {$table29} WHERE id = %d",
                        $row->id
                    )
                );
            }

            return ["completed" => $from_date1];
        }

        $image_base64_data = $this->getParamValue("image_base64_data");
        if (Helper::is_valid_string($image_base64_data)) {
            // Validate and decode base64 image data
            $data = explode( ',', $image_base64_data );
            if (count($data) < 2) {
                throw new ServiceException(esc_html("Invalid image data format", 'flyerimage'));
            }

            // Validate the data URL scheme and extract type
            if (!preg_match('/^data:image\/(png|jpe?g|gif|webp|svg\+xml|svg)(?:;[^;]+)*;base64$/i', trim($data[0]), $matches)) {
                throw new ServiceException(esc_html("Invalid image type. Only PNG, JPEG, GIF, SVG and WebP images are allowed.", 'flyerimage'));
            }
            $type = strtolower($matches[1]);

            // Map to file extension
            $ext = 'png';
            if ($type === 'svg+xml' || $type === 'svg') {
                $ext = 'svg';
            } elseif ($type === 'jpeg' || $type === 'jpg') {
                $ext = 'jpg';
            } elseif ($type === 'webp') {
                $ext = 'webp';
            } elseif ($type === 'gif') {
                $ext = 'gif';
            } elseif ($type === 'png') {
                $ext = 'png';
            }

            // Build file name and path
            $imageName = gmdate("Y-m-d") . "-" . time() . "." . $ext;
            $output_file = $flyerimage_upload_dir . DIRECTORY_SEPARATOR . $imageName;
            
            // Decode base64 data
            $decoded = base64_decode( $data[1], true );
            
            // Validate base64 decoding was successful
            if ($decoded === false) {
                throw new ServiceException(esc_html("Failed to decode image data", 'flyerimage'));
            }
            
            // Validate decoded data is not empty
            if (empty($decoded)) {
                throw new ServiceException(esc_html("Decoded image data is empty", 'flyerimage'));
            }
            
            $success = $wp_filesystem->put_contents(
                $output_file,
                $decoded,
                FS_CHMOD_FILE
            );
            
            if ( ! $success ) {
                throw new ServiceException(esc_html("Failed to save Flyer image", 'flyerimage'));
            }

            // Validate the saved file is actually a valid image
            // Try getimagesize first (works for raster formats). For SVGs getimagesize returns false,
            // so fall back to basic SVG detection and validation by inspecting file contents.
            $image_info = @getimagesize($output_file);

            $width = 0;
            $height = 0;
            $mime = null;

            if ($image_info !== false) {
                [$width, $height] = $image_info;
                $mime = isset($image_info['mime']) ? $image_info['mime'] : null;
            }
            else {
                // getimagesize failed — attempt to detect SVG
                $file_contents = @file_get_contents($output_file);
                $is_svg = false;
                $svg_width = null;
                $svg_height = null;

                if ($file_contents !== false && preg_match('/<svg\b[^>]*>/i', $file_contents)) {
                    // Basic sanitization: reject obvious script-like content
                    $lower = strtolower($file_contents);
                    if (strpos($lower, '<script') === false && strpos($lower, 'javascript:') === false && !preg_match('/on[a-z]+\s*=/i', $file_contents)) {
                        $is_svg = true;

                        // Try to extract width/height attributes (numbers, optional px)
                        if (preg_match('/<svg[^>]*\bwidth\s*=\s*["\']?([0-9.]+)(?:px)?["\']?/i', $file_contents, $m)) {
                            $svg_width = (int) round((float)$m[1]);
                        }
                        if (preg_match('/<svg[^>]*\bheight\s*=\s*["\']?([0-9.]+)(?:px)?["\']?/i', $file_contents, $m)) {
                            $svg_height = (int) round((float)$m[1]);
                        }

                        // Fallback to viewBox (min-x min-y width height)
                        if (($svg_width === null || $svg_height === null) && preg_match('/viewBox\s*=\s*["\']\s*[-0-9.]+\s+[-0-9.]+\s+([0-9.]+)\s+([0-9.]+)\s*["\']/i', $file_contents, $m)) {
                            if ($svg_width === null) { $svg_width = (int) round((float)$m[1]); }
                            if ($svg_height === null) { $svg_height = (int) round((float)$m[2]); }
                        }

                        // Provide sensible defaults if dimensions are still unknown
                        if ($svg_width === null) { $svg_width = 100; }
                        if ($svg_height === null) { $svg_height = 100; }

                        $width = $svg_width;
                        $height = $svg_height;
                        $mime = 'image/svg+xml';
                    }
                }

                if (! $is_svg) {
                    // Delete the invalid file
                    $wp_filesystem->delete($output_file);
                    throw new ServiceException(esc_html("Invalid image file. The uploaded data is not a valid image.", 'flyerimage'));
                }
            }

            if ($width <= 0 || $height <= 0) {
                // Delete the invalid file
                $wp_filesystem->delete($output_file);
                throw new ServiceException(esc_html("Invalid image dimensions", 'flyerimage'));
            }

            // Additional security: Verify MIME type is an allowed image type (include SVG)
            $allowed_mime_types = array('image/png', 'image/jpeg', 'image/gif', 'image/webp', 'image/svg+xml');

            if ($mime === null && function_exists('mime_content_type')) {
                $mime = mime_content_type($output_file);
            }

            if (!in_array($mime, $allowed_mime_types, true)) {
                // Delete the invalid file
                $wp_filesystem->delete($output_file);
                throw new ServiceException(esc_html("Invalid image type. Only PNG, JPEG, GIF, SVG and WebP are allowed.", 'flyerimage'));
            }

            $params = ["image_path" => $imageName, "created_at" => gmdate("Y-m-d")];
            $id = QueryBuilder::table(ITables::$FLYER_IMAGES_HISTORY)->create($params);
            if ($id) {
                return [
                    "image_id" => $id,
                    "image_path" => $imageName,
                    "image_url" => $upload_dir['baseurl'] . "/flyerimage/" . $imageName,
                    "width" => $width,
                    "height" => $height
                ];
            }
        }
        throw new ServiceException(esc_html__("Failed to save Flyer image", 'flyerimage'));
    }

    public function list_internal_pages() {
        $this->wpFilterCallback = new WpFilterCallback($this->params);

        try {
            add_filter( 'posts_where', [$this->wpFilterCallback, 'wp_posts_filter_posts_where_query'], 10, 2 );
            add_filter( 'posts_fields', [$this->wpFilterCallback, 'wp_posts_filter_posts_fields_query'], 10, 2 );

            $page = $this->getParamValue("page_number", 1);
            $page = Helper::is_positive_integer($page) ? intval($page) : 1;
            $page = $page >= 1 ? $page : 1;

            $limit = 200;

            $args = array( 
                'post_type'         => array( 'page', 'post' ),
                'orderby'           => 'ID',
                'order'             => 'ASC',
                'offset'            => ($page - 1) * $limit,
                'posts_per_page'    => $limit,
                // array('publish', 'pending', 'draft', 'auto-draft', 'future', 'private', 'inherit', 'trash')
                'post_status'       => array('publish')
            );
            // https://developer.wordpress.org/reference/classes/wp_query/
            $the_query = new \WP_Query( $args );
            $total = $the_query->found_posts;
            $posts = [];
            if ($page == 1) {
                $posts[] = [
                    "id" => "-1", "title" => __("Home Page", 'flyerimage'),
                    "type" => "page", "post_name" => ""
                ];
            }
            foreach($the_query->posts as $post) {
                $posts[] = [
                    "id" => $post->ID, "title" => $post->post_title,
                    "type" => $post->post_type, "post_name" => "/".$post->post_name
                ];
            }
            return [
                'currentPage' => $page,
                'numOfPages'  => ceil($total / $limit),
                'showingFrom' => (($page - 1) * $limit) + 1,
                'showingTo'   => (($page - 1) * $limit) + count($posts),
                'has_next'    => $total > ($page * $limit),
                'has_prev'    => ($page - 1) > 0,
                'itemCount'   => intval($total),
                'list'        => $posts,
            ];
        }
        finally {
            remove_filter( 'posts_where', [$this->wpFilterCallback, 'wp_posts_filter_posts_where_query'], 10 );
            remove_filter( 'posts_fields', [$this->wpFilterCallback, 'wp_posts_filter_posts_fields_query'], 10 );
        }
    }

    public function list_post_categories() {
        $list = [];
        foreach ( get_categories() as $item ) {
            $list[] = ["id" => $item->cat_ID, "title" => $item->name];
        }
        return [
            'currentPage' => 1,
            'numOfPages'  => 1,
            'showingFrom' => 1,
            'showingTo'   => count($list),
            'has_next'    => false,
            'has_prev'    => false,
            'itemCount'   => count($list),
            'list'        => $list,
        ];
    }

    public function change_flyer_status() {
        $edit_id = Helper::is_valid_table_id($this->getParamString("id", "", 20)) ? $this->getParamString("id", "", 20) : null;
        if (!$edit_id) {
            throw new ServiceException(esc_html__('Campaign could not found', 'flyerimage'));
        }

        $status = $this->getParamString("status", "");
        if ($status == "delete") {
            $existing_images = QueryBuilder::table(ITables::$FLYER_IMAGES_SCREENS)->where(["flyer_image_id" => $edit_id])->get();

            QueryBuilder::table(ITables::$FLYER_IMAGES, 'FI')->where(['FI.id' => $edit_id])->delete("FI");
            QueryBuilder::table(ITables::$FLYER_IMAGES_SCREENS, 'FI')->where(['FI.flyer_image_id' => $edit_id])->delete("FI");
            QueryBuilder::table(ITables::$FLYER_IMAGES_PAGES, 'FI')->where(['FI.flyer_image_id' => $edit_id])->delete("FI");
            QueryBuilder::table(ITables::$FLYER_IMAGES_REPORT_FLYER_OPEN, 'FI')->where(['FI.flyer_image_id' => $edit_id])->delete("FI");

            QueryBuilder::table(ITables::$FLYER_IMAGES_HISTORY)
                ->where(['used_by' => $edit_id])->update([
                    "used_by" => 0
                ]);

            return true;
        }
        else if ($status == "inactive") {
            QueryBuilder::table(ITables::$FLYER_IMAGES, 'FI')->where(['FI.id' => $edit_id])->update(["FI.status" => "inactive"]);
            return true;
        }
        else if ($status == "active") {
            QueryBuilder::table(ITables::$FLYER_IMAGES, 'FI')->where(['FI.id' => $edit_id])->update(["FI.status" => "active"]);
            return true;
        }
        else if ($status == "duplicate") {
            $this->setParams(["edit_id" => $edit_id]);
            $form = $this->create_configuration();
            if (isset($form["invalid"])) {
                throw new ServiceException(esc_html__('Campaign could not found', 'flyerimage'));
            }
            $form = json_decode(json_encode($form["form"]), true);

            $form["duplicate_id"] = $form["id"]; unset($form["id"]);

            $title = $form["title"];
            $copy_index = 1;
            while (true) {
                $duplicate = QueryBuilder::table($this->table_name, 'FI')
                    ->select(['FI.id'])
                    ->where(['title' => $title]);
                if (count($duplicate->get()) > 0) {
                    if ($copy_index == 1) {
                        $title = $form["title"] . " Copy";
                    }
                    else {
                        $title = $form["title"] . " Copy ($copy_index)";
                    }
                    $copy_index++;
                }
                else {
                    break;
                }
            }
            $form["title"] = $title;

            $screens = isset($form["screens"]) ? $form["screens"] : [];
            foreach ($screens as &$screen) {
                $this->setParams($screen);
                if (isset($screens["id"])) unset($screen["id"]);

                $background_image = $this->getParamValue("background_image");
                if (!is_null($background_image) && is_array($background_image)) {
                    if (isset($background_image["image_id"])) {
                        unset($screen["background_image"]["image_id"]);
                    }
                }

                $elements = isset($screen["elements"]) ? $screen["elements"] : [];
                foreach ($elements as &$element) {
                    if (isset($element["id"])) unset($element["id"]);
                    if (isset($element["image_id"])) unset($element["image_id"]);
                }
                $screen["elements"] = $elements;
            }
            $form["screens"] = $screens;

            $this->setParams($form);
            $output = $this->save_flyer();
            if (!isset($output["id"])) {
                throw new ServiceException(esc_html__('Campaign could not duplicated', 'flyerimage'));
            }
            $output["id"] = strval($output["id"]);
            return $output;
        }
    }

    public function get_flyer_for_preview() {
        $id = $this->getParamString("id", "");
        if (Helper::is_valid_table_id($id)) {
            $entity = QueryBuilder::table($this->table_name, 'FLYER0')
                ->select([
                    'FLYER0.id', 'FLYER0.status', 'FLYER0.title', 'FLYER0.start_date', 'FLYER0.start_time', 'FLYER0.end_date',
                    'FLYER0.end_time', 'FLYER0.flyer_configs'
                ])
                ->where(['FLYER0.id' => $id])
                ->get();
            if (count($entity) == 0) {
                return ["invalid" => 1];
            }

            $flyer_configs = json_decode($entity[0]->flyer_configs, true);

            $record = [
                "status" => $entity[0]->status,
                "title" => $entity[0]->title,
                "start_date" => $entity[0]->start_date,
                "end_date" => $entity[0]->end_date,
            ];
            foreach ($flyer_configs as $k => $v) {
                $record[$k] = $v;
                if ($k == "pages" || $k == "categories") {
                    $record[$k] = Helper::is_valid_table_id_concat($record[$k]) ? explode(",", $record[$k]) : [];
                }
            }

            $record["site_url"] = FLYER_IMAGE_URL;
            $record["base_url"] = get_site_url();

            $screen = QueryBuilder::table(ITables::$FLYER_IMAGES_SCREENS, 'FI_IMAGES')
                ->select(['FI_IMAGES.*'])
                ->where(['FI_IMAGES.flyer_image_id' => $entity[0]->id])
                ->first();

            if (is_null($screen)) {
                return ["invalid" => 1];
            }
            else {
                $screen->elements = json_decode($screen->elements, true);
                $screen->background_image = json_decode($screen->background_image, true);
                $record["screen"] = $screen;
            }

            return $record;
        }
        return ["invalid" => 1];
    }

    public function get_flyer_for_main_page_display() {
        
        $tz = $this->getParamString("tz", date_default_timezone_get(), 120);
        try {
            $tz_obj = new \DateTimeZone($tz);
        } catch (\Exception $e) {
            $tz_obj = new \DateTimeZone('UTC');
        }
        $dt = new \DateTime('now', $tz_obj);

        $today = $dt->format("Y-m-d");
        $time = "1" . $dt->format("Hi");

        $site_url = get_site_url();

        $post_id = $this->getParamString("post_id", "0", 20);
        $post_id = Helper::is_valid_table_id($post_id) ? $post_id : 0;

        $post_url = $this->getParamString("url", "");
        if ($post_url == $site_url || $post_url == $site_url . "/") {
            $post_id = -1;
        }

        $entity = QueryBuilder::table($this->table_name, 'FLYER0')
            ->where("FLYER0.status='active' AND (FLYER0.show_for_all_pages=1 OR PAGES0.value=$post_id OR FLYER0.campaign_version='3')")
            ->limit(1);

        $entity->left_join(ITables::$FLYER_IMAGES_PAGES . " as PAGES0", "(PAGES0.flyer_image_id=FLYER0.id AND PAGES0.type IN ('page','post'))");

        $entity->where("(FLYER0.start_date IS NULL OR FLYER0.start_date<'$today' OR (FLYER0.start_date='$today' AND FLYER0.start_time IS NULL) OR (FLYER0.start_date='$today' AND FLYER0.start_time<='$time') )");
        $entity->where("(FLYER0.end_date IS NULL OR FLYER0.end_date>'$today' OR (FLYER0.end_date='$today' AND FLYER0.end_time IS NULL) OR (FLYER0.end_date='$today' AND FLYER0.end_time>='$time') )");
        $entity->select(['FLYER0.id', 'FLYER0.title', 'FLYER0.flyer_configs']);
        $entity->order_by("FLYER0.updated_at DESC");
        $entity = $entity->get();

        $flyer = ["post_id" => $post_id, "site_url" => $site_url];
        if (count($entity) > 0) {
            $entity = $entity[0];
            $configs = Helper::parse_json_string($entity->flyer_configs, []);
            if (count($configs) > 0) {
                $flyer["id"]               = $entity->id;
                $flyer["site_url"]         = FLYER_IMAGE_URL;
                $flyer["base_url"]         = get_site_url();
                $flyer["campaign_version"] = $configs["campaign_version"] ?? "2";

                $device_type = $this->getParamString("device_type", "unknown");

                if ( ( $configs["campaign_version"] ?? "" ) === "3" ) {
                    // ── v3 campaign ──────────────────────────────────────
                    $v3 = $configs["v3_canvas"] ?? null;
                    if ( $v3 ) {
                        // Device targeting
                        $targets = $v3["devicesTarget"] ?? [ "desktop" => true, "mobile" => true ];
                        $is_mobile  = ( $device_type === "mobile" );
                        $is_tablet  = ( $device_type === "tablet" );
                        $is_desktop = ( ! $is_mobile && ! $is_tablet );
                        $allowed    = ( $is_mobile  && ! empty( $targets["mobile"] ) )
                                   || ( $is_tablet  && ! empty( $targets["tablet"] ) )
                                   || ( $is_desktop && ! empty( $targets["desktop"] ) );
                        if ( $allowed ) {
                            $flyer["v3_canvas"] = $v3;
                        }
                    }
                } else {
                    // ── v2 campaign ──────────────────────────────────────
                    $flyer["close_automatically"]      = $configs["close_automatically"];
                    $flyer["close_flyer_after_seconds"] = $configs["close_flyer_after_seconds"];
                    $flyer["show_close_icon"]          = $configs["show_close_icon"];

                    $screen = QueryBuilder::table(ITables::$FLYER_IMAGES_SCREENS, 'FI_IMAGES')
                        ->select(['FI_IMAGES.*'])->where(['FI_IMAGES.flyer_image_id' => $flyer["id"]])
                        ->where(["screen_name", "=", $device_type])
                        ->order_by("FI_IMAGES.screen_width DESC")
                        ->first();

                    if (!is_null($screen)) {
                        $screen->elements = json_decode($screen->elements, true);
                        $screen->background_image = json_decode($screen->background_image, true);
                        $flyer["screen"] = $screen;
                    }
                }
            }
        }

        /*$url = $this->getParamString("url", "", 3000);
        $parsed_url = wp_parse_url( $url );
        $slug = substr($parsed_url['path'], 1 );
        $post_id = url_to_postid( $slug );
        $analog_id = 0;
        if ( ! $post_id ) { // If it didn't work try to get it manually from DB
            global $wpdb;
            $post_query_result = $wpdb->get_row("SELECT ID FROM {$wpdb->prefix}posts WHERE post_name = '{$slug}'");
            $analog_id = (int) $post_query_result->ID;
        }*/

        return $flyer;
    }

    public function register_flyer_report() {
        foreach ($this->params as $param) {
            $this->params = $param;

            $action = mb_strtolower($this->getParamString("action", "", 100));
            if (!in_array($action, [
                "open", "screen_open", "link_click",
                "close_button_click", "close_automatically"
            ])) {
                return 0;
            }
            $insert = [
                "flyer_image_id" => $this->getParamString("flyer_image_id", "0", 20),
                "flyer_image_screen_id" => $this->getParamString("flyer_image_screen_id", "0", 20),
                "action" => $action,
                "report_date" => gmdate("Y-m-d"),
                "report_hour" => gmdate("H"),
                "report_month" => gmdate("Y-m")."-01",
                "report_year" => gmdate("Y")."-01-01",
                "display_time" => intval($this->getParamString("display_time", 0, 5)),
                "page_url" => $this->getParamString("page_url", "", 2000),
                "page_title" => $this->getParamString("page_title", "", 500),
                "time_zone" => $this->getParamString("tz", "", 50),
                "session_id" => $this->get_session_id(),
                "ip_address" => $this->get_user_ip_address(),
                "country_name" => Helper::get_country_by_ip($this->get_user_ip_address()),
                "device_type" => $this->getParamString("device", "", 50),
                "os_name" => $this->getParamString("os_name", "", 50),
                "browser_name" => $this->getParamString("browser", "", 50),
            ];
            QueryBuilder::table(ITables::$FLYER_IMAGES_REPORT_FLYER_OPEN)->create($insert);
        }
        return 1;
    }

    private function get_user_ip_address() {
        if ( ! empty( $_SERVER['HTTP_CLIENT_IP'] ) ) {
            return sanitize_text_field( wp_unslash( $_SERVER['HTTP_CLIENT_IP'] ) );
        }
        elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
            return sanitize_text_field( wp_unslash( $_SERVER['HTTP_X_FORWARDED_FOR'] ) );
        }
        else if (isset($_SERVER['REMOTE_ADDR'])) {
            return sanitize_text_field( wp_unslash( $_SERVER['REMOTE_ADDR'] ) );
        }
        return "";
    }

    private function get_session_id() {
        // Check if session is already started
        if (session_status() === PHP_SESSION_NONE) {
            // Only start session if not in WordPress admin or AJAX context
            if (!is_admin() && !wp_doing_ajax()) {
                session_start();
            }
        }
        
        $session_id = session_id();

        // If no PHP session, try to get from request parameter
        if (!Helper::is_valid_string($session_id)) {
            $session_id = $this->getParamString("session_id", "", 50);
        }
        
        // Fallback to a generated unique identifier based on user IP and user agent
        if (!Helper::is_valid_string($session_id)) {
            $ip = $this->get_user_ip_address();
            $user_agent = isset($_SERVER['HTTP_USER_AGENT']) ? sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT'])) : '';
            $session_id = md5($ip . $user_agent . gmdate('Y-m-d'));
        }
        
        return $session_id;
    }

    private function load_templates() {
        global $wpdb;

        $table = self::get_table(ITables::$FLYER_TEMPLATES);

        $rows = $wpdb->get_results("SELECT CAST(id AS CHAR) AS id,template_name,template_description FROM {$table}");

        return $rows;
    }

    public function submit_form() {
        // ── Honeypot check: bots fill hidden fields, real users never do ──
        $honeypot = $this->getParamString("_hp", "", 20);
        if ( $honeypot !== '' ) {
            // Silently succeed — bot doesn't know it was blocked
            return ['success' => true];
        }

        // ── IP rate limiting: max 10 submissions per IP per hour ──
        $ip         = isset($_SERVER['HTTP_X_FORWARDED_FOR'])
            ? sanitize_text_field(explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'])[0])
            : sanitize_text_field($_SERVER['REMOTE_ADDR'] ?? '');
        $rate_key   = 'fi_sub_rate_' . md5($ip);
        $rate_limit = 10;
        $rate_window = HOUR_IN_SECONDS;

        $hits = (int) get_transient($rate_key);
        if ( $hits >= $rate_limit ) {
            throw new ServiceException("Too many submissions. Please try again later.");
        }
        // Increment counter; set expiry only on first hit
        if ( $hits === 0 ) {
            set_transient($rate_key, 1, $rate_window);
        } else {
            // Preserve remaining TTL by updating the value only
            set_transient($rate_key, $hits + 1, $rate_window);
        }

        $flyer_image_id = intval($this->getParamValue("flyer_image_id", 0));
        if ( ! $flyer_image_id ) {
            throw new ServiceException("Invalid campaign");
        }

        // Verify campaign exists — prevents data injection into non-existent IDs
        global $wpdb;
        $tbl      = self::get_table(ITables::$FLYER_IMAGES);
        $exists   = $wpdb->get_var($wpdb->prepare("SELECT id FROM `{$tbl}` WHERE id = %d LIMIT 1", $flyer_image_id));
        if ( ! $exists ) {
            throw new ServiceException("Invalid campaign");
        }

        // Validate URL scheme — only http/https accepted
        $page_url = $this->getParamString("page_url", "", 500);
        $scheme   = parse_url($page_url, PHP_URL_SCHEME);
        if ( ! in_array($scheme, ['http', 'https'], true) ) {
            $page_url = '';
        }

        $fields = $this->getParamValue("fields", "");
        if ( ! is_string($fields) ) {
            $fields = json_encode($fields);
        }
        if ( strlen($fields) > 10000 ) {
            throw new ServiceException("Payload too large");
        }

        $insert = [
            "flyer_image_id" => $flyer_image_id,
            "submitted_at"   => current_time('mysql'),
            "page_url"       => $page_url,
            "fields"         => $fields,
        ];
        QueryBuilder::table(ITables::$FLYER_IMAGE_SUBMISSIONS)->create($insert);

        return ['success' => true];
    }

    public function get_submissions() {
        global $wpdb;

        $page     = max(1, intval($this->getParamValue("page", 1)));
        $per_page = max(1, intval($this->getParamValue("per_page", 20)));
        $offset   = ($page - 1) * $per_page;

        $submissions_table = self::get_table(ITables::$FLYER_IMAGE_SUBMISSIONS);
        $flyer_table       = self::get_table(ITables::$FLYER_IMAGES);

        $where_clause = "1=1";
        $where_args   = [];

        $flyer_image_id = intval($this->getParamValue("flyer_image_id", 0));
        if ($flyer_image_id > 0) {
            $where_clause .= " AND S.flyer_image_id = %d";
            $where_args[] = $flyer_image_id;
        }

        $count_query = "SELECT COUNT(*) FROM `{$submissions_table}` S WHERE {$where_clause}";
        $total = $flyer_image_id > 0
            ? intval($wpdb->get_var($wpdb->prepare($count_query, ...$where_args)))
            : intval($wpdb->get_var($count_query));

        $data_query = "SELECT S.id, S.flyer_image_id, FI.title AS campaign_title, S.submitted_at, S.page_url, S.fields
                       FROM `{$submissions_table}` S
                       LEFT JOIN `{$flyer_table}` FI ON FI.id = S.flyer_image_id
                       WHERE {$where_clause}
                       ORDER BY S.submitted_at DESC
                       LIMIT %d OFFSET %d";

        $query_args = array_merge($where_args, [$per_page, $offset]);
        $rows = $wpdb->get_results($wpdb->prepare($data_query, ...$query_args));

        foreach ($rows as &$row) {
            if (!is_null($row->fields) && is_string($row->fields)) {
                $decoded = json_decode($row->fields, true);
                $row->fields = is_array($decoded) ? (object) $decoded : new \stdClass();
            }
        }

        return [
            'data'     => $rows,
            'total'    => $total,
            'page'     => $page,
            'per_page' => $per_page,
        ];
    }
}
