<?php

namespace MDirectorNewsletter\includes;

if ( ! defined( 'ABSPATH' ) ) {
	exit;
}

use EmblueNewsletter\Log\Log;
use MDirectorNewsletter\admin\MDirector_Newsletter_Admin;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
use Twig\Error\SyntaxError;
use Twig\TemplateWrapper;

require_once(plugin_dir_path(__FILE__) . '../vendor/autoload.php');

/**
 * Fired during plugin activation.
 *
 *
 * @since      1.0.0
 * @package    Mdirector_Newsletter
 * @subpackage Mdirector_Newsletter/includes
 * @author     MDirector
 */
class Mdirector_Newsletter_Utils
{
    // Configs
    const SHOW_SEGMENTS_IN_USER_INTERFACE = true;

    // ========================================================================
    // CONFIGURACIÓN DE AMBIENTE: QA vs PRODUCCIÓN
    // ========================================================================
    // TODO: CAMBIAR A PRODUCCIÓN ANTES DE RELEASE
    // Para cambiar a producción, comenta la línea de QA y descomenta la de PROD
    // ========================================================================
    const USE_QA_ENVIRONMENT = false;  // ← Cambiar a false para PRODUCCIÓN
    
    // URLs de API
    const MDIRECTOR_API_URL_QA = 'https://apiqa.embluemail.com/v2.3i';
    const MDIRECTOR_API_URL_PROD = 'https://api.embluemail.com/v2.3i';
    
    // URL activa según ambiente
    const MDIRECTOR_MAIN_URL = self::USE_QA_ENVIRONMENT 
        ? self::MDIRECTOR_API_URL_QA 
        : self::MDIRECTOR_API_URL_PROD;
    
    // Endpoints (se construyen dinámicamente según la URL activa)
    const MDIRECTOR_API_DELIVERY_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/actions/action';
    const MDIRECTOR_API_CONTACT_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/contacts';
    const MDIRECTOR_API_CONTACT_TO_GROUP_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/groups/manage_contacts';
    const MDIRECTOR_API_TEST_CONTACTS_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/contacts/test_contacts';
    const MDIRECTOR_API_TEST_GROUPS_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/groups/test_groups';
    const MDIRECTOR_API_GROUPS_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/groups';
    const MDIRECTOR_API_GROUPS_LIST_ENDPOINT = self::MDIRECTOR_API_GROUPS_ENDPOINT . '/query';
    const MDIRECTOR_API_CAMPAIGNS_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/campaigns';
    const MDIRECTOR_API_CAMPAIGNS_LIST_ENDPOINT = self::MDIRECTOR_API_CAMPAIGNS_ENDPOINT . '/query';
    const MDIRECTOR_API_SENDERS_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/senders/query';
    
    // Endpoint legacy (por compatibilidad)
    const MDIRECTOR_API_CAMPAIGN_ENDPOINT = self::MDIRECTOR_MAIN_URL . '/api_campaign';

    const TEMPLATES_PATH = MDIRECTOR_TEMPLATES_PATH . self::DEFAULT_TEMPLATE . '/';
    const TEMPLATE_HTML_BASE_FILE = 'template.html';

    // Language / templates
    const MDIRECTOR_LANG_DOMAIN = 'emblue-newsletter';
    const MDIRECTOR_DEFAULT_USER_LANG = 'es';
    const DEFAULT_TEMPLATE = 'default';
    const MAX_IMAGE_SIZE_MDIRECTOR_TEMPLATE = 143;
    const MAX_IMAGE_SIZE_DEFAULT_TEMPLATE = '100%';

    // Newsletter settings
    const DAILY_FREQUENCY = 'daily';
    const WEEKLY_FREQUENCY = 'weekly';
    const DEFAULT_DAILY_MAIL_SUBJECT = 'Daily mail';
    const DEFAULT_WEEKLY_MAIL_SUBJECT = 'Weekly mail';
    const DYNAMIC_SUBJECT = 'dynamic';
    const FIXED_SUBJECT = 'fixed';
    const DYNAMIC_CRITERIA_FIRST_POST = 'first_post';
    const DYNAMIC_CRITERIA_LAST_POST = 'last_post';
    const SETTINGS_OPTION_ON = 'yes';
    const SETTINGS_OPTION_OFF = 'no';
    const MIDNIGHT_HOUR = '00:00';
    const EXCERPT_LENGTH_CHARS = 200;
    const MDIRECTOR_SCHEDULER_LISTS_FIELD = 'mdirector-scheduler-lists';
    const MDIRECTOR_SCHEDULER_SEGMENTS_FIELD = 'mdirector-scheduler-segments';
    const MDIRECTOR_SCHEDULER_GROUPS_FIELD = 'mdirector-scheduler-groups';

    // Prefixes
    const FORM_PREFIX = 'mdirector_widget-';
    const DAILY_WEEKS_ALLOWED_PREFIX = 'mdirector_daily_weekday_';
    const FORM_CLASS = 'md__newsletter--form';
    const FORM_NAME = self::FORM_PREFIX . 'form';
    const SUBSCRIBE_NONCE_ACTION = 'emblue_subscribe_action';

    const USER_SEGMENT = 'segment';
    const USER_LIST = 'list';
    const USER_GROUP = 'group';
    const API_LIST_KEYWORD = '';
    const API_GROUP_KEYWORD = '';
    const MDIRECTOR_WEEKLY_CAMPAIGN = 'mdirector_weekly_campaign_';
    const MDIRECTOR_WEEKLY_CAMPAIGN_TESTS = 'mdirector_weekly_campaign_tests';
    const MDIRECTOR_DAILY_CAMPAIGN = 'mdirector_daily_campaign_';
    const MDIRECTOR_DAILY_CAMPAIGN_TESTS = 'mdirector_daily_campaign_tests';

    // LOG TYPES
    const LOG_DEBUG = 'DEBUG';
    const LOG_INFO = 'INFO';
    const LOG_ERROR = 'ERROR';
    const LOG_NOTICE = 'NOTICE';

    /**
     * @var Mdirector_Newsletter_Twig
     */
    protected $twigInstance;

    /**
     * @var \Twig\Environment
     */
    protected $twigUserTemplate;

    /**
     * @var \Twig\TemplateWrapper
     */
    protected $adminTemplate;

    protected $pluginNotices = [];

    /**
     * Mdirector_Newsletter_Utils constructor.
     *
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function __construct()
    {
        $this->twigInstance = new Mdirector_Newsletter_Twig();
        $this->adminTemplate = $this->twigInstance->initAdminTemplate();
    }

    /**
     * Public subscription forms must always use a guest nonce (user ID 0),
     * otherwise cached pages and logged-in visitors produce invalid_nonce errors.
     */
    public static function createPublicSubscribeNonce() {
        $action = self::SUBSCRIBE_NONCE_ACTION;
        $uid    = 0;
        $token  = '';
        $tick   = wp_nonce_tick( $action );

        return substr( wp_hash( $tick . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ), -12, 10 );
    }

    /**
     * @param string $nonce Nonce value from the subscription request.
     */
    public static function verifyPublicSubscribeNonce( $nonce ) {
        $action = self::SUBSCRIBE_NONCE_ACTION;
        $uid    = 0;
        $token  = '';
        $nonce  = (string) $nonce;

        if ( '' === $nonce ) {
            return false;
        }

        $tick = wp_nonce_tick( $action );

        for ( $offset = 0; $offset <= 2; $offset++ ) {
            $expected = substr(
                wp_hash( ( $tick - $offset ) . '|' . $action . '|' . $uid . '|' . $token, 'nonce' ),
                -12,
                10
            );

            if ( hash_equals( $expected, $nonce ) ) {
                return true;
            }
        }

        return false;
    }

    /**
     * Allowed HTML for the plugin's admin panels and subscription forms.
     *
     * Used with wp_kses() at every output point. The markup is produced by the
     * plugin's own Twig templates (autoescape enabled), but per WordPress guidelines
     * all output must still pass through wp_kses(). wp_kses_post() strips form
     * controls, so we extend the "post" allow-list with the form tags/attributes
     * actually used by the plugin templates.
     *
     * wp_kses() is called directly at each echo (not via a wrapper) so that both
     * PHP_CodeSniffer and the Plugin Check "late_escaping" sniff recognise it.
     *
     * @return array Allow-list for wp_kses().
     */
    public static function allowedHtml() {
        // wp_kses() strips inline CSS properties not in WordPress' safe list
        // (notably `display` and `overflow-x`, used by the admin templates to
        // toggle/hide elements). Allow them for the plugin's own output.
        // WordPress de-duplicates identical callbacks, so this registers once.
        add_filter( 'safe_style_css', [ __CLASS__, 'allowStyleCss' ] );

        $allowed = wp_kses_allowed_html( 'post' );

        // Form controls not included in the "post" context.
        $allowed['form']     = [];
        $allowed['input']    = [
            'type'     => true, 'name'    => true, 'value'   => true, 'placeholder' => true,
            'checked'  => true, 'disabled' => true, 'readonly' => true, 'required'  => true,
            'min'      => true, 'max'     => true, 'step'    => true,
        ];
        $allowed['select']   = [ 'name' => true, 'multiple' => true, 'disabled' => true ];
        $allowed['option']   = [ 'value' => true, 'selected' => true, 'disabled' => true ];
        $allowed['optgroup'] = [ 'label' => true ];
        $allowed['textarea'] = [ 'name' => true, 'rows' => true, 'cols' => true, 'placeholder' => true ];
        $allowed['button']   = [ 'type' => true, 'name' => true, 'value' => true, 'disabled' => true ];
        $allowed['label']    = [ 'for' => true ];
        $allowed['fieldset'] = [];
        $allowed['legend']   = [];
        $allowed['details']  = [ 'open' => true ];
        $allowed['summary']  = [];

        // Common attributes used across template tags (avoid stripping styling/JS hooks).
        $common = [
            'class' => true, 'id'    => true, 'style'  => true, 'title'  => true,
            'href'  => true, 'target' => true, 'name'  => true, 'for'    => true,
            'data-toggle' => true, 'data-value' => true, 'data-type' => true, 'data-related' => true,
        ];
        foreach ( $allowed as $tag => $attrs ) {
            $allowed[ $tag ] = array_merge( (array) $attrs, $common );
        }

        return $allowed;
    }

    /**
     * Adds CSS properties used by the plugin templates to wp_kses()'s safe list.
     *
     * @param array $styles Allowed CSS property names.
     * @return array
     */
    public static function allowStyleCss( $styles ) {
        $styles[] = 'display';
        $styles[] = 'overflow';
        $styles[] = 'overflow-x';

        return $styles;
    }

    public function getRegisterFormHTML($args = [], $instance = null)
    {
        extract($args, EXTR_SKIP);
        $options = $this->getPluginOptions();

        $MDirectorActive = get_option('mdirector_active');
        $output = '';

        if (!isset($beforeTitle)) {
            $beforeTitle = null;
        }

        if (!isset($afterTitle)) {
            $afterTitle = null;
        }

        if (empty($options['mdirector_frequency_daily'])
            && empty($options['mdirector_frequency_weekly'])) {
            return false;
        }

        $title = empty($instance['title'])
            ? ' ' : apply_filters('widget_title', $instance['title']);

        if (!empty($title)) {
            $output .= $beforeTitle . wp_kses_post( $title ) . $afterTitle;
        }

        if (!empty($description)) {
            $output .= '<p class="md__newsletter--description">'
                . esc_html( $instance['description'] ) . '</p>';
        }

        if ($MDirectorActive === self::SETTINGS_OPTION_ON) {
            if (!empty($options['mdirector_bearer_token'])) {
                $selectFrequency = $this->getSelectFrequency();
                $currentLang = $this->getCurrentLang();
                $currentPrivacyText = 'mdirector_policy_text_' . $currentLang;
                $currentPrivacyUrl = 'mdirector_policy_url_' . $currentLang;

                $accept = (isset($options[$currentPrivacyText])
                    && $options[$currentPrivacyText] != '')
                    ? $options[$currentPrivacyText]
                    : __(
                        'WIDGET-PRIVACY__POLICY__ACCEPTED',
                        'emblue-newsletter'
                    );

                $mdPrivacyLink = (isset($options[$currentPrivacyUrl])
                    && $options[$currentPrivacyUrl] != '')
                    ? $options[$currentPrivacyUrl] : '#';

                $spinnerPath =
                    MDIRECTOR_NEWSLETTER_PLUGIN_URL . 'assets/ajax-loader.png';

                $templateData = [
                    'formClass'       => self::FORM_CLASS,
                    'formName'        => self::FORM_NAME,
                    'formPrefix'      => self::FORM_PREFIX,
                    'selectFrequency' => $selectFrequency,
                    'isSpinner'       => file_exists( $spinnerPath ),
                    'spinnerPath'     => $spinnerPath,
                    'privacyLink'     => $mdPrivacyLink,
                    'privacyLinkText' => $accept,
                    'userLang'        => $this->getCurrentLang(),
                    'inputPlaceholder' => __( 'WIDGET-EMAIL', 'emblue-newsletter' ),
                    'actionButton'    => __( 'WIDGET-SUBSCRIPTION', 'emblue-newsletter' ),
                    'nonceField'      => self::createPublicSubscribeNonce(),
                ];
                $output .= $this->adminTemplate->renderBlock(
                    'subscriptionForm',
                    $templateData
                );
            }
        }

        return $output;
    }

    public function getPluginOptions()
    {
        return get_option('mdirector_settings')
            ? get_option('mdirector_settings') : [];
    }

    private function getSelectFrequency()
    {
        $options = $this->getPluginOptions();
        $selectFrequency = null;

        $templateData = [
            'fieldName' => self::FORM_PREFIX
        ];

        if ($options['mdirector_frequency_daily'] === self::SETTINGS_OPTION_ON
            && $options['mdirector_frequency_weekly']
            === self::SETTINGS_OPTION_ON) {
            $selectFrequency =
                $this->adminTemplate->renderBlock(
                    'selectFrequencyLayer',
                    $templateData
                );
        } else {
            if ($options['mdirector_frequency_daily']
                === self::SETTINGS_OPTION_ON) {
                $templateData['value'] = self::DAILY_FREQUENCY;
                $selectFrequency =
                    $this->adminTemplate->renderBlock(
                        'singleFrequencyLayer',
                        $templateData
                    );
            } else {
                if ($options['mdirector_frequency_weekly']
                    === self::SETTINGS_OPTION_ON) {
                    $templateData['value'] = self::WEEKLY_FREQUENCY;
                    $selectFrequency =
                        $this->adminTemplate->renderBlock(
                            'singleFrequencyLayer',
                            $templateData
                        );
                }
            }
        }

        return $selectFrequency;
    }

    public function getCurrentLang()
    {
        if ($this->isWPML()) {
            return apply_filters('wpml_current_language', null); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML's hook name, not ours.
        }

        if (!empty($this->getCurrentLanguages())) {
            $languages = $this->getCurrentLanguages();
            $firstLang = reset($languages);
            if (isset($firstLang['code'])) {
                return $firstLang['code'];
            }
        }

        return self::MDIRECTOR_DEFAULT_USER_LANG;
    }

    public function isWPML()
    {
        return function_exists('icl_object_id');
    }

    public function getCurrentLanguages()
    {
        if ($this->isWPML()) {
            // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML's hook name, not ours.
            $languages = apply_filters(
                'wpml_active_languages', null,
                'orderby=id&order=desc'
            );
            // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound

            if (
                !empty($languages) &&
                $this->findKeyInMultiArray($languages, 'code') &&
                $this->findKeyInMultiArray($languages, 'translated_name')
            ) {
                return $languages;
            }
        }

        $defaultName = explode('_', get_locale())[0];
        return [
            $defaultName => [
                'code' => $defaultName,
                'translated_name' => __(
                    'DEFAULT-LANGUAGE',
                    'emblue-newsletter'
                )
            ]
        ];
    }

    public function findKeyInMultiArray($array, $keySearch)
    {
        foreach ($array as $key => $item) {
            if ($key == $keySearch) {
                return true;
            } elseif (is_array($item) && $this->findKeyInMultiArray($item, $keySearch)) {
                return true;
            }
        }

        return false;
    }

    public function getTemplateOptionsHTML($availableTemplates, $currentTemplateSelected)
    {
        $output = '';

        foreach ($availableTemplates as $template) {
            $baseTemplateName = basename($template);
            $isSelected = ($baseTemplateName === $currentTemplateSelected);

            $templateData = [
                'key' => $baseTemplateName,
                'isSelected' => $isSelected,
                'value' => $baseTemplateName
            ];

            $output .= $this->adminTemplate->renderBlock(
                'buildOption',
                $templateData
            );
        }

        return $output;
    }

    public function resetDeliveriesSent()
    {
        $options = $this->getPluginOptions();

        $options['mdirector_daily_sent'] = null;
        $options['mdirector_weekly_sent'] = null;

        $this->updateMDirectorSettingsWithLog($options, 'Reset deliveries sent...');
    }


    public function updateMDirectorSettingsWithLog($options, $msg)
    {
        $this->log(
            $msg,
            ['options' => $options],
            self::LOG_INFO
        );
        update_option('mdirector_settings', $options);
    }

    public function log($message, $context, $type = self::LOG_DEBUG)
    {
        $options = $this->getPluginOptions();
        if (isset($options['mdirector_logs_deactivated'])
            && $options['mdirector_logs_deactivated'] === self::SETTINGS_OPTION_ON) {
            return true;
        }

        $context = $this->redactSensitive($context);

        switch ($type) {
            case self::LOG_INFO:
                Log::info($message, $context);
                break;
            case self::LOG_NOTICE:
                Log::notice($message, $context);
                break;
            case self::LOG_ERROR:
                Log::error($message, $context);
                break;
            case self::LOG_DEBUG:
            default:
                Log::debug($message, $context);
                break;
        }
    }

    /**
     * Masks sensitive credentials (API token/secret) before they reach the log,
     * so activity logs never store secrets. Recurses into nested arrays.
     *
     * @param mixed $data Log context.
     * @return mixed
     */
    private function redactSensitive($data)
    {
        if (!is_array($data)) {
            return $data;
        }

        $sensitiveKeys = [
            'mdirector_bearer_token',
            'mdirector_api',
            'mdirector_secret',
            'mdirector_token',
        ];

        foreach ($data as $key => $value) {
            if (is_array($value)) {
                $data[$key] = $this->redactSensitive($value);
            } elseif (in_array($key, $sensitiveKeys, true)
                && is_string($value) && $value !== '') {
                $data[$key] = '***REDACTED***';
            }
        }

        return $data;
    }

    /**
     * @return string
     */
    public function getWeeklyCampaignsByLanguageHTML()
    {
        return $this->getCampaignsHTMLByFrequency(self::MDIRECTOR_WEEKLY_CAMPAIGN);
    }

    private function getCampaignsHTMLByFrequency($frequency)
    {
        $campaigns = [];
        $options = $this->getPluginOptions();
        $availableCampaigns = $this->getUserCampaignsByAPI();

        foreach ($this->getCurrentLanguages() as $language => $languageName) {
            $campaignFieldName = $frequency . $language;
            $currentCampaignSelected = isset($options[$campaignFieldName])
                ? $options[$campaignFieldName] : null;

            $templateCampaignSelectorData = [
                'campaignFieldName' => $campaignFieldName,
                'campaignsHMTL' => $this->getUserCampaignsHTML($availableCampaigns, $currentCampaignSelected)
            ];

            $templateCampaignBlockData = [
                'langName' => $languageName['translated_name'],
                'campaignSelector' => $this->adminTemplate->renderBlock(
                    'campaignSelector',
                    $templateCampaignSelectorData
                )
            ];

            $campaigns[] = $this->adminTemplate->renderBlock('campaignLanguageBlock', $templateCampaignBlockData);
        }

        return implode('', $campaigns);
    }

    /**
     * Obtiene las campañas disponibles desde la API de emBlue
     * Utiliza el endpoint POST /campaigns/query de la API v2.3i
     * 
     * @return array|false Array de campañas o false en caso de error
     */
    public function getUserCampaignsByAPI()
    {       
        $options = $this->getPluginOptions();
        $MDirectorActive = get_option('mdirector_active');
       
        if ($MDirectorActive == self::SETTINGS_OPTION_ON) {
            $MDirectorAPI = new Mdirector_Newsletter_Api();
            $token = $options['mdirector_bearer_token'];
            
            // Llamada al endpoint POST /campaigns/query (similar al patrón de grupos)
            $APIData = [
                "search" => "",    // String vacío = devuelve TODAS las campañas disponibles
                "limit" => 1000    // Límite máximo de la API
            ];
            
            $response = $MDirectorAPI->callAPI(
                $token,
                self::MDIRECTOR_API_CAMPAIGNS_LIST_ENDPOINT,
                'POST',
                $APIData
            );

            if (is_wp_error($response)) {
                $this->log(
                    'Error retrieving campaigns via API: ' . $response->get_error_message(),
                    [],
                    self::LOG_ERROR
                );
                return false;
            }

            $MDirectorAPIResponse = json_decode($response);
            
            if (!is_array($MDirectorAPIResponse)) {
                $this->log(
                    'Error decoding campaigns API response',
                    ['response' => substr($response, 0, 200)],
                    self::LOG_ERROR
                );
                return false;
            }

            return $MDirectorAPIResponse;
        }

        return false;
    }

    public function getUserCampaignsHTML($availableCampaigns, $currentCampaignSelected)
    {
        $output = '';
        if (empty($availableCampaigns) || !is_array($availableCampaigns)) {
            return $output;
        }

        foreach ($availableCampaigns as $campaign) {
            // La API v2.3i devuelve 'campaignId' y 'name' (no 'id' y 'campaignName')
            $campaignId = isset($campaign->campaignId) ? $campaign->campaignId : (isset($campaign->id) ? $campaign->id : null);
            $campaignName = isset($campaign->name) ? $campaign->name : (isset($campaign->campaignName) ? $campaign->campaignName : null);
            
            $isSelected = intval($campaignId) === intval($currentCampaignSelected);
            $templateData = [
                'key' => $campaignId,
                'isSelected' => $isSelected,
                'value' => '(' . $campaignId . ') ' . $campaignName
            ];

            $output .= $this->adminTemplate->renderBlock(
                'buildOption',
                $templateData
            );
        }

        return $output;
    }

    public function getWeeklyCampaignsTestsHTML()
    {
        return $this->getCampaignsForTestsHTMLByFrequency(self::MDIRECTOR_WEEKLY_CAMPAIGN_TESTS);
    }

    private function getCampaignsForTestsHTMLByFrequency($frequency)
    {
        $options = $this->getPluginOptions();
        $availableCampaigns = $this->getUserCampaignsByAPI();
        $currentCampaignSelected =
            isset($options[$frequency]) ? $options[$frequency] : null;
        $templateData = [
            'campaignFieldName' => $frequency,
            'campaignsHMTL' => $this->getUserCampaignsHTML($availableCampaigns, $currentCampaignSelected)
        ];

        return $this->adminTemplate->renderBlock('campaignSelector', $templateData);
    }

    /**
     * @return string
     */
    public function getDailyCampaignsByLanguageHTML()
    {
        return $this->getCampaignsHTMLByFrequency(self::MDIRECTOR_DAILY_CAMPAIGN);
    }

    public function getDailyCampaignsTestsHTML()
    {
        return $this->getCampaignsForTestsHTMLByFrequency(self::MDIRECTOR_DAILY_CAMPAIGN_TESTS);
    }

    /**
     * @return string
     */
    public function getUserListsHTML($currentListsSelected = [])
    {
        $lists = $this->getUserListsByAPI();
        return $this->getListsHTML($lists, $currentListsSelected, self::USER_LIST);
    }

    /**
     * Obtiene los senders (remitentes) disponibles desde la API de emBlue
     * Utiliza el endpoint POST /senders/query de la API v2.3i
     * 
     * @return array|false Array de senders o false en caso de error
     */
    public function getUserSendersByAPI()
    {
        $options = $this->getPluginOptions();
        $MDirectorActive = get_option('mdirector_active');

        if ($MDirectorActive == self::SETTINGS_OPTION_ON) {
            $MDirectorAPI = new Mdirector_Newsletter_Api();
            $this->bearerToken = $options['mdirector_bearer_token'];
            
            // Llamada al endpoint POST /senders/query (similar al patrón de grupos)
            $APIData = [
                "search" => "",   // String vacío = devuelve TODOS los senders disponibles
                "limit" => 1000   // Límite máximo de la API
            ];
            
            $response = $MDirectorAPI->callAPI(
                $this->bearerToken, 
                self::MDIRECTOR_API_SENDERS_ENDPOINT, 
                'POST',
                $APIData
            );

            if (is_wp_error($response)) {
                $this->log(
                    'Error retrieving senders via API: ' . $response->get_error_message(),
                    [],
                    self::LOG_ERROR
                );
                return false;
            }

            $MDirectorAPIResponse = json_decode($response);
            
            // Si la respuesta viene envuelta en un objeto con data
            if (is_object($MDirectorAPIResponse) && isset($MDirectorAPIResponse->data)) {
                return is_array($MDirectorAPIResponse->data) ? $MDirectorAPIResponse->data : false;
            }
            
            // Si es un array directo
            if (is_array($MDirectorAPIResponse)) {
                return $MDirectorAPIResponse;
            }

            $this->log(
                'Error decoding senders API response',
                ['response' => substr($response, 0, 200)],
                self::LOG_ERROR
            );
            
            return false;
        }

        return false;
    }

    /**
     * Obtiene los grupos de prueba disponibles desde la API de emBlue
     * Utiliza el endpoint POST /groups/test_groups de la API v2.3i
     * Devuelve IDs de grupo, nombres y cantidad de contactos (nivel usuario, no cuenta)
     *
     * @return array|false Array de grupos de prueba o false en caso de error
     */
    public function getUserTestGroupsByAPI()
    {
        $options = $this->getPluginOptions();
        $MDirectorActive = get_option('mdirector_active');

        if ($MDirectorActive == self::SETTINGS_OPTION_ON) {
            $MDirectorAPI = new Mdirector_Newsletter_Api();
            $token = $options['mdirector_bearer_token'];

            $response = $MDirectorAPI->callAPI(
                $token,
                self::MDIRECTOR_API_TEST_GROUPS_ENDPOINT,
                'POST',
                []
            );

            if (is_wp_error($response)) {
                $this->log(
                    'Error retrieving test groups via API: ' . $response->get_error_message(),
                    [],
                    self::LOG_ERROR
                );
                return false;
            }

            $MDirectorAPIResponse = json_decode($response);

            if (!is_array($MDirectorAPIResponse)) {
                $this->log(
                    'Error decoding test groups API response',
                    ['response' => substr($response, 0, 200)],
                    self::LOG_ERROR
                );
                return false;
            }

            return $MDirectorAPIResponse;
        }

        return false;
    }

    /**
     */
    public function getUserListsByAPI()
    {        
        $options = $this->getPluginOptions();
        $MDirectorActive = get_option('mdirector_active');

        if ($MDirectorActive == self::SETTINGS_OPTION_ON) {
            $MDirectorAPI = new Mdirector_Newsletter_Api();
            $this->bearerToken = $options['mdirector_bearer_token'];
            $APIData = [
                "search" => "",
                "limit" => 100
            ];
            $response = $MDirectorAPI->callAPI($this->bearerToken, self::MDIRECTOR_API_GROUPS_LIST_ENDPOINT, 'POST', $APIData);                  

            if (is_wp_error($response)) {
                $this->log(
                    'Error retrieving lists via API: ' . $response->get_error_message(),
                    [],
                    self::LOG_ERROR
                );
                return false;
            }
            $MDirectorAPIResponse =json_decode($response);     
            if (!is_array($MDirectorAPIResponse)) {
                $this->log(
                    'Error decoding API response',
                    [],
                    self::LOG_ERROR
                );
                return false;
            }

            return $MDirectorAPIResponse;
        }

        return false;
    }

    /**
     * Genera el HTML del dropdown de senders para daily o weekly
     * 
     * @param string $frequency 'daily' o 'weekly'
     * @return string HTML del dropdown con los senders disponibles
     */
    public function getSendersDropdownHTML($frequency)
    {
        $options = $this->getPluginOptions();
        $availableSenders = $this->getUserSendersByAPI();
        $fieldName = 'mdirector_sender_' . $frequency;
        $currentSenderSelected = $options[$fieldName] ?? null;
        
        $output = '<select id="' . esc_attr($fieldName) . '" ';
        $output .= 'name="' . esc_attr($fieldName) . '" ';
        $output .= 'class="sender-selector field-selector">';
        
        // Opción por defecto
        $output .= '<option value="">' . __('SELECT-SENDER', 'emblue-newsletter') . '</option>';
        
        if (!empty($availableSenders) && is_array($availableSenders)) {
            foreach ($availableSenders as $sender) {
                // Ajustar según estructura real de la API
                $senderId = $sender->id ?? $sender->senderId ?? null;
                $senderName = $sender->name ?? $sender->senderName ?? '';
                $senderEmail = $sender->emailFrom ?? $sender->email ?? '';
                
                if (empty($senderId)) {
                    continue; // Saltar si no tiene ID
                }
                
                $selected = (intval($senderId) === intval($currentSenderSelected)) ? ' selected="selected"' : '';
                
                $displayName = $senderName;
                if (!empty($senderEmail)) {
                    $displayName .= ' <' . $senderEmail . '>';
                }
                
                $output .= '<option value="' . esc_attr($senderId) . '"' . $selected . '>';
                $output .= esc_html($displayName);
                $output .= '</option>';
            }
        } else {
            $output .= '<option value="" disabled>' . __('NO-SENDERS-AVAILABLE', 'emblue-newsletter') . '</option>';
        }
        
        $output .= '</select>';
        
        return $output;
    }

    /**
     * Genera el HTML del dropdown de grupos de prueba
     * La API devuelve: groupId, name, contactsQuantity
     *
     * @return string HTML del dropdown con los grupos de prueba disponibles
     */
    public function getTestGroupsDropdownHTML()
    {
        $options = $this->getPluginOptions();
        $availableTestGroups = $this->getUserTestGroupsByAPI();
        $fieldName = 'mdirector_test_groups';
        $currentGroupSelected = $options[$fieldName] ?? '';

        $output = '<select id="' . esc_attr($fieldName) . '" ';
        $output .= 'name="' . esc_attr($fieldName) . '" ';
        $output .= 'class="test-groups-selector field-selector">';

        $output .= '<option value="">-- ' . __('SELECT-TEST-GROUP', 'emblue-newsletter') . ' --</option>';

        if (!empty($availableTestGroups) && is_array($availableTestGroups)) {
            foreach ($availableTestGroups as $group) {
                $groupId = $group->groupId ?? null;
                $groupName = $group->name ?? '';

                if (empty($groupId)) {
                    continue;
                }

                $selected = ($groupId == $currentGroupSelected) ? ' selected="selected"' : '';
                $contactsQty = intval($group->contactsQuantity ?? 0);
                $label = esc_html($groupName) . ' (' . $contactsQty . ')';

                $output .= '<option value="' . esc_attr($groupId) . '"' . $selected . '>';
                $output .= $label;
                $output .= '</option>';
            }
        } else {
            $output .= '<option value="" disabled>' . __('NO-TEST-GROUPS-AVAILABLE', 'emblue-newsletter') . '</option>';
        }

        $output .= '</select>';

        return $output;
    }

    private function getListsHTML(
        $lists,
        $currentListsSelected,
        $type = self::USER_LIST,
        $fieldName = null,
        $lang = null
    ) {
        if ($type !== self::USER_LIST) {
            return '';
        }

        return $this->getSelect2HTML($lists, $currentListsSelected, $fieldName, $lang);
    }

    private function getSelect2HTML($lists, $currentListsSelected, $fieldName, $lang)
    {
        $listsFieldName  = $this->getUserListNameByType(self::USER_LIST,  $fieldName, $lang);
        $groupsFieldName = $this->getUserListNameByType(self::USER_GROUP, $fieldName, $lang);

        $selectedLists  = isset($currentListsSelected[self::USER_LIST])  && is_array($currentListsSelected[self::USER_LIST])  ? array_map('strval', $currentListsSelected[self::USER_LIST])  : [];
        $selectedGroups = isset($currentListsSelected[self::USER_GROUP]) && is_array($currentListsSelected[self::USER_GROUP]) ? array_map('strval', $currentListsSelected[self::USER_GROUP]) : [];

        $listsId  = 'md-select2-' . md5($listsFieldName);
        $groupsId = 'md-select2-' . md5($groupsFieldName);

        $output = '<div class="md-select2-wrapper">';

        // --- Grupos (top-level lists) ---
        $output .= '<div class="md-select2-section">';
        $output .= '<select multiple id="' . esc_attr($listsId) . '" name="' . esc_attr($listsFieldName) . '[]" class="md-select2" data-placeholder="' . esc_attr(__('Buscar grupos...', 'emblue-newsletter')) . '">';
        if (!empty($lists)) {
            foreach ($lists as $list) {
                $value    = (string) $this->getUserListValueByType($list, self::USER_LIST);
                $selected = in_array($value, $selectedLists, true) ? ' selected' : '';
                $output  .= '<option value="' . esc_attr($value) . '"' . $selected . '>' . esc_html($list->name) . '</option>';
            }
        }
        $output .= '</select></div>';

        if (!self::SHOW_SEGMENTS_IN_USER_INTERFACE) {
            $output .= '</div>';
            return $output;
        }

        // --- Subgrupos ---
        $groupsHtml = '';
        if (!empty($lists)) {
            foreach ($lists as $list) {
                if (!empty($list->segment_group)) {
                    $groupsHtml .= '<optgroup label="' . esc_attr($list->name) . '">';
                    foreach ($list->segment_group as $group) {
                        $value       = (string) $this->getUserListValueByType($group, self::USER_GROUP);
                        $selected    = in_array($value, $selectedGroups, true) ? ' selected' : '';
                        $groupsHtml .= '<option value="' . esc_attr($value) . '"' . $selected . '>' . esc_html($group->name) . '</option>';
                    }
                    $groupsHtml .= '</optgroup>';
                }
            }
        }

        if (!empty($groupsHtml)) {
            $output .= '<div class="md-select2-section">';
            $output .= '<select multiple id="' . esc_attr($groupsId) . '" name="' . esc_attr($groupsFieldName) . '[]" class="md-select2" data-placeholder="' . esc_attr(__('Buscar subgrupos...', 'emblue-newsletter')) . '">';
            $output .= $groupsHtml;
            $output .= '</select></div>';
        }

        $output .= '</div>';

        return $output;
    }

    private function getUserListNameByType($type, $fieldName, $lang)
    {
        switch ($type) {
            case self::USER_SEGMENT:
                return $fieldName
                    ? $fieldName . self::USER_SEGMENT . '_' . $lang
                    : self::MDIRECTOR_SCHEDULER_SEGMENTS_FIELD;
            case self::USER_GROUP:
                return $fieldName
                    ? $fieldName . self::USER_GROUP . '_' . $lang
                    : self::MDIRECTOR_SCHEDULER_GROUPS_FIELD;
            case self::USER_LIST:
            default:
                return $fieldName
                    ? $fieldName . $lang
                    : self::MDIRECTOR_SCHEDULER_LISTS_FIELD;
        }
    }

    private function getUserListValueByType($item, $type)
    {
        return $type !== self::USER_GROUP
            ? ($item->id ?: $item->segId)
            : $item->gruId;
    }

    private function getUserListExtraData($item, $type)
    {
        if ($type !== self::USER_GROUP) {
            return '';
        }

        return join(';', $this->getSegmentsInGroup($item));
    }

    private function getSegmentsInGroup($item)
    {
        $segments = $item->segments;

        return array_map([$this, 'getSegmentId'], $segments);
    }

    public function getSelectedUserListsByLanguageHTML($frequency = self::DAILY_FREQUENCY, $flags = null)
    {
        $options = $this->getPluginOptions();
        $userLists = [];

        foreach ($this->getCurrentLanguages() as $language) {
            $lang = $language['code'];
            $lists = $this->getListsPrefix($frequency, $flags);
            $listsFieldName = $lists . $lang;
            $groupsFieldName = $lists . self::USER_GROUP . '_' . $lang;
            $segmentsFieldName = $lists . self::USER_SEGMENT . '_' . $lang;

            $availableLists = $this->getUserListsByAPI();
                        
            $currentListsSelected = [
                self::USER_LIST => isset($options[$listsFieldName])
                    ? $options[$listsFieldName] : [],
                self::USER_GROUP => isset($options[$groupsFieldName])
                    ? $options[$groupsFieldName] : [],
                self::USER_SEGMENT => isset($options[$segmentsFieldName])
                    ? $options[$segmentsFieldName] : []
            ];

            $templateData = [
                'langName' => $language['translated_name'],
                'listsFieldName' => $listsFieldName,
                'listsCount' => is_array($availableLists) ? count($availableLists) : 0,
                'listsHMTL' => $this->getListsHTML(
                    $availableLists, $currentListsSelected, self::USER_LIST, $lists, $lang
                )
            ];

            $userLists[] = $this->adminTemplate->renderBlock(
                'listSelector',
                $templateData
            );
        }

        return implode('', $userLists);
    }

    private function getListsPrefix($frequency, $flags)
    {
        $lists = $frequency === Mdirector_Newsletter_Utils::DAILY_FREQUENCY
            ? 'mdirector_daily_list_'
            : 'mdirector_weekly_list_';

        if ($flags === MDirector_Newsletter_Admin::TEST_FLAG) {
            $lists .= 'test_';
        }

        return $lists;
    }

    public function getAndParseCustomPost($postId)
    {
        $foundPosts = $this->getCustomPost($postId);

        return array_map([$this, 'parsePost'], $foundPosts);
    }

    public function getCustomPost($postId)
    {
        $args = $this->getCustomPostArg($postId);
        $lang = self::MDIRECTOR_DEFAULT_USER_LANG;

        return $this->getPosts($args, $lang);
    }

    private function getCustomPostArg($postID)
    {
        return [
            'p' => $postID,
            'post_type' => 'any'
        ];
    }

    private function getPosts($args, $lang)
    {
        // phpcs:disable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML hook names, not ours.
        do_action('wpml_switch_language', $lang);
        $query = new \WP_Query($args);
        do_action('wpml_switch_language', $this->getCurrentLang());
        // phpcs:enable WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound

        return $query->posts;
    }

    /**
     * @param bool $previewMode
     * @param bool $isTestMode
     *
     * @return array
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function buildDailyMails($previewMode = false, $isTestMode = false, $forceRealDeliveryMode = false)
    {
        $response = [];
        $delivery = 0;
        $isAlreadySentInOtherLanguage = false;

        foreach ($this->getCurrentLanguages() as $language) {
            $lang = $language['code'];

            if ($this->isDeliveryActive($lang, self::DAILY_FREQUENCY, $isTestMode)) {
                if ($delivery > 0) {
                    $isAlreadySentInOtherLanguage = true;
                }

                $response[$lang] = $this->mdSendDailyMails(
                    $lang, $previewMode, $isAlreadySentInOtherLanguage, $isTestMode, $forceRealDeliveryMode
                );

                // TODO: This solution is not accurate
                if (!$response[$lang]) {
                    break;
                }

                $delivery++;
            }
        }

        return $response;
    }

    private function isDeliveryActive($lang, $type, $isTestMode = false)
    {
        $options = $this->getPluginOptions();
        $listForDelivery = 'mdirector_' . $type . '_list_';
        $segmentForDelivery = $listForDelivery;
        $groupForDelivery = $listForDelivery;

        if ($this->hasToUseTestList($options, $isTestMode)) {
            $listForDelivery .= 'test_' . $lang;
            $segmentForDelivery .= 'test_segment_' . $lang;
            $groupForDelivery .= 'test_group_' . $lang;
            return (
                (isset($options[$listForDelivery]) && !empty($options[$listForDelivery])) ||
                (isset($options[$segmentForDelivery]) && !empty($options[$segmentForDelivery])) ||
                (isset($options[$groupForDelivery]) && !empty($options[$groupForDelivery]))
            );
        }
        $listForDelivery .= $lang;
        $segmentForDelivery .= 'segment_' . $lang;
        $groupForDelivery .= 'group_' . $lang;

        $activationField = 'mdirector_frequency_' . $type;
        return (
            ($options[$activationField] === self::SETTINGS_OPTION_ON) &&
            (
                (isset($options[$listForDelivery]) && !empty($options[$listForDelivery])) ||
                (isset($options[$segmentForDelivery]) && !empty($options[$segmentForDelivery])) ||
                (isset($options[$groupForDelivery]) && !empty($options[$groupForDelivery]))
            )
        );
    }

    private function hasToUseTestList($options, $isTestMode = false)
    {
        return $isTestMode || isset($options['mdirector_use_test_lists']) &&
            $options['mdirector_use_test_lists'] === self::SETTINGS_OPTION_ON;
    }

    /**
     * @param $lang
     * @param $previewMode
     * @param $isAlreadySentInOtherLanguage
     * @param $isTestMode
     * @param $forceRealDeliveryMode
     *
     * @return bool
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    private function mdSendDailyMails(
        $lang,
        $previewMode,
        $isAlreadySentInOtherLanguage = false,
        $isTestMode = false,
        $forceRealDeliveryMode = false
    ) {
        $options = $this->getPluginOptions();

        $hour = ($options['mdirector_hour_daily'])
            ?: self::MIDNIGHT_HOUR;
        $timeExploded = explode(':', $hour);
        $actualTime = gmdate('Y-m-d H:i:s');
        $mailSent = gmdate(
            'Y-m-d', strtotime(
                isset($options['mdirector_daily_sent'])
                    ? $options['mdirector_daily_sent'] : null
            )
        );

        $canSend = $this->checkIfDeliveryCanBeSent($mailSent);

        $fromDate =
            $this->calculateFromDate($timeExploded, self::DAILY_FREQUENCY);
        $toDate = $this->calculateToDate($timeExploded);
        $this->log(
            'mdSend Daily Mails',
            [
                'previewMode' => $previewMode,
                'isTestMode' => $isTestMode,
                'isAlreadySentInOtherLanguage' => $isAlreadySentInOtherLanguage,
                'forceRealDeliveryMode' => $forceRealDeliveryMode
            ],
            self::LOG_INFO
        );
        if ($previewMode ||
            $forceRealDeliveryMode ||
            $isTestMode ||
            $isAlreadySentInOtherLanguage ||
            (strtotime($actualTime) >= strtotime($toDate) && $canSend)) {

            $args = $this->getPostsArgs($fromDate, $toDate);

            if (!empty($excludeCats = $this->getExcludeCats())) {
                $args['cat'] = implode(', ', $excludeCats);
            }

            $posts = $this->buildPosts($this->getPosts($args, $lang), $lang);

            if (!empty($posts)) {
                !$previewMode && !$isTestMode && $this->cleanNewsletterProcess(self::DAILY_FREQUENCY);
                if ($this->mdSendMail($posts, self::DAILY_FREQUENCY, $lang, $previewMode, $isTestMode)) {
                    return true;
                }

                // Process failed; reset last time sent...
                $this->log('Daily delivery failed...', [], self::LOG_ERROR);
                $this->resetNewsletterDeliveryDateSent(self::WEEKLY_FREQUENCY);
                return false;
            }

            $this->log(
                'Daily delivery aborted: there are no new posts for daily mails.',
                [],
                self::LOG_ERROR
            );

            $this->log(
                'There are no new posts for daily mails and lang ' . $lang,
                $args,
                self::LOG_NOTICE
            );
        } else {
            $this->log(
                "Daily delivery can not be sent (last delivery was $mailSent)",
                [
                    'Actual time' => $actualTime,
                    'Next delivery schedule' => $toDate,
                    'strTime' => strtotime($actualTime),
                    'strDate' => strtotime($toDate),
                    'canSend' => $canSend
                ],
                self::LOG_NOTICE
            );
        }

        return false;
    }

    private function checkIfDeliveryCanBeSent($mailSent)
    {
        $options = $this->getPluginOptions();
        $currentDayOption =
            self::DAILY_WEEKS_ALLOWED_PREFIX . strtolower(gmdate('D'));

        if (isset($options[$currentDayOption])
            && $options[$currentDayOption] !== self::SETTINGS_OPTION_ON) {
            return false;
        }

        return $mailSent !== gmdate('Y-m-d');
    }

    protected function calculateFromDate($time, $frequency)
    {
        $daysToSubtract = $frequency === self::DAILY_FREQUENCY ? 1 : 7;

        return gmdate(
            'Y-m-d H:i:s',
            mktime(
                $time[0], $time[1], 00,
                gmdate('m'), gmdate('d') - $daysToSubtract, gmdate('Y')
            )
        );
    }

    protected function calculateToDate($time)
    {
        return gmdate(
            'Y-m-d H:i:s',
            mktime(
                $time[0], $time[1], 00,
                gmdate('m'), gmdate('d'), gmdate('Y')
            )
        );
    }

    private function getPostsArgs($fromDate, $toDate)
    {
        return [
            'post_type' => 'post',
            'post_status' => 'publish',
            'date_query' => [
                'after' => $fromDate,
                'before' => $toDate
            ],
            'nopaging ' => true
        ];
    }

    private function getExcludeCats()
    {
        $options = $this->getPluginOptions();

        $excludeCats = ($options['mdirector_exclude_cats'])
            ? unserialize($options['mdirector_exclude_cats'])
            : [];

        if (count($excludeCats) > 0) {
            for ($i = 0; $i < count($excludeCats); $i++) {
                $excludeCats[$i] = -1 * abs($excludeCats[$i]);
            }
        }

        return $excludeCats;
    }

    private function buildPosts($foundPosts, $lang)
    {
        $options = $this->getPluginOptions();
        $totalFoundPosts = count($foundPosts);
        $minimumEntries = $options['mdirector_minimum_entries'];

        if (
            is_numeric($minimumEntries) && $totalFoundPosts < $minimumEntries
        ) {
            $complementaryPosts =
                $this->getMinimumPostsForNewsletter(
                    $totalFoundPosts,
                    $minimumEntries, $lang
                );
            $foundPosts = array_merge($foundPosts, $complementaryPosts);
        }

        return array_map([$this, 'parsePost'], $foundPosts);
    }

    private function getMinimumPostsForNewsletter(
        $total_found_posts,
        $minimum_entries,
        $lang
    ) {
        $remainingItems = $minimum_entries - $total_found_posts;

        $args = [
            'post_type' => 'post',
            'post_status' => 'publish',
            'nopaging ' => true,
            'offset' => $total_found_posts,
            'posts_per_page' => $remainingItems
        ];

        if (!empty($excludeCats = $this->getExcludeCats())) {
            $args['cat'] = implode(', ', $excludeCats);
        }

        return $this->getPosts($args, $lang);
    }

    public function cleanNewsletterProcess($frequency)
    {
        $options = $this->getPluginOptions();
        $process = ($frequency === self::DAILY_FREQUENCY)
            ? 'mdirector_daily_sent'
            : 'mdirector_weekly_sent';

        $options[$process] = gmdate('Y-m-d H:i');

        $this->updateMDirectorSettingsWithLog($options, 'Clean Newsletter Process');

        wp_reset_postdata();
    }

    /**
     * @param        $posts
     * @param        $frequency
     * @param string $lang
     * @param false $previewMode
     * @param false $isTestMode
     * @param null $template
     * @param null $campaign
     * @param array $lists
     * @param array $segments
     * @param array $groups
     * @param null $from
     * @param null $subject
     *
     * @return bool
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function mdSendMail(
        $posts,
        $frequency,
        $lang = self::MDIRECTOR_DEFAULT_USER_LANG,
        $previewMode = false,
        $isTestMode = false,
        $template = null,
        $campaign = null,
        $lists = [],
        $segments = [],
        $groups = [],
        $from = null,
        $subject = null,
        $delivery = null
    ) {
        add_filter('wp_mail_content_type', [$this, 'setHTMLContentType']);

        if (!empty($posts)) {
            if (empty($template)) {
                $template = $this->getTemplateName($lang);
            }

            $mailSubject = $subject ?:
                $this->composeEmailSubject($posts, $frequency, $lang);

            $templatePath = $this->getTemplatePath($template);
            $templateURL = $this->getTemplateURL($template);
            $templateMainFile = $this->getTemplateMainFile($templatePath);

            $templateData = [
                'templateURL' => $templateURL,
                'templatePath' => $templatePath,
                'header_title' => get_bloginfo('name'),
                'site_link' => get_bloginfo('url'),
                'posts' => $posts
            ];

            $this->twigUserTemplate =
                $this->twigInstance->initUserTemplate($templateData);
            $this->twigUserTemplate->load($templateMainFile);

            $mailContent = ($this->templateMainFileIsTwig($templateMainFile))
                ? $this->twigUserTemplate->render(
                    Mdirector_Newsletter_Twig::USER_TEMPLATE,
                    $templateData
                )
                : $this->parsingTemplate($templateData);

            if ($previewMode) {
                // Full HTML email document cannot pass through wp_kses (it would strip
                // <html>/<head>/<style>). Render it inside a sandboxed iframe instead,
                // escaping the whole document with esc_attr() on the srcdoc attribute.
                echo '<!DOCTYPE html><html><head><meta charset="utf-8"><title>' .
                    esc_html__( 'Newsletter preview', 'emblue-newsletter' ) .
                    '</title></head><body style="margin:0">';
                echo '<iframe sandbox title="' .
                    esc_attr__( 'Newsletter preview', 'emblue-newsletter' ) .
                    '" style="width:100%;height:100vh;border:0" srcdoc="' .
                    esc_attr( $mailContent ) . '"></iframe>';
                echo '</body></html>';
                exit();
            }

            return $this->sendMailAPI(
                $mailContent, $mailSubject, $isTestMode, $frequency,
                $lang, $campaign, $lists, $segments, $groups, $from, $delivery
            );
        }

        return false;
    }

    public function getTemplateName($lang)
    {
        $templatesAvailable = $this->getPluginTemplates();

        return $this->getCurrentTemplate($templatesAvailable, $lang);
    }

    public function getPluginTemplates()
    {
        $options = $this->getPluginOptions();
        $customTemplates = $this->getUserCustomTemplates();

        if (isset($options['mdirector_hide_sample_templates']) &&
            $options['mdirector_hide_sample_templates'] === self::SETTINGS_OPTION_ON) {
            return $customTemplates;
        }

        $defaultTemplates = $this->getDefaultTemplates();
        return array_merge($defaultTemplates, $customTemplates);
    }

    private function getUserCustomTemplates()
    {
        $customTemplatesOnChildThemePath = $this->getCustomTemplatesOnChildThemePath();
        $customTemplatesOnChildTheme = array_map(
            'basename',
            glob($customTemplatesOnChildThemePath . '*', GLOB_ONLYDIR)
        );

        $customTemplatesOnParentThemePath = $this->getCustomTemplatesOnParentThemePath();
        $customTemplatesOnParentTheme = array_map(
            'basename',
            glob($customTemplatesOnParentThemePath . '*', GLOB_ONLYDIR)
        );

        return array_merge($customTemplatesOnChildTheme, $customTemplatesOnParentTheme);
    }

    private function getCustomTemplatesOnChildThemePath()
    {
        return get_stylesheet_directory()
            . DIRECTORY_SEPARATOR
            . MDIRECTOR_NEWSLETTER
            . DIRECTORY_SEPARATOR;
    }

    private function getCustomTemplatesOnParentThemePath()
    {
        return get_template_directory()
            . DIRECTORY_SEPARATOR
            . MDIRECTOR_NEWSLETTER
            . DIRECTORY_SEPARATOR;
    }

    private function getDefaultTemplates()
    {
        return array_map(
            'basename',
            glob(MDIRECTOR_TEMPLATES_PATH . '*', GLOB_ONLYDIR)
        );
    }

    public function getCurrentTemplate($available_templates, $lang = null)
    {
        $options = $this->getPluginOptions();

        $template = 'mdirector_template_';
        $lang = is_array($lang) ? $lang['code'] : $lang;

        if (isset($options[$template . $lang])) {
            $currentTemplateSelected = $options[$template . $lang];
        } else {
            if (isset($options[$template . 'general'])) {
                $currentTemplateSelected = $options[$template . 'general'];
            } else {
                return Mdirector_Newsletter_Utils::DEFAULT_TEMPLATE;
            }
        }

        if (!in_array($currentTemplateSelected, $available_templates)) {
            return Mdirector_Newsletter_Utils::DEFAULT_TEMPLATE;
        }

        return $currentTemplateSelected;
    }

    private function composeEmailSubject($posts, $frequency, $lang)
    {
        $options = $this->getPluginOptions();

        if ($frequency === self::DAILY_FREQUENCY) {
            $subject = isset($options['mdirector_subject_type_daily']) &&
            ($options['mdirector_subject_type_daily'] === self::DYNAMIC_SUBJECT)
                ? $options['mdirector_subject_dynamic_prefix_daily_' . $lang]
                . ' ' .
                $this->getDynamicPostTitle(
                    $posts,
                    $options['mdirector_subject_dynamic_value_daily']
                )
                : $options['mdirector_subject_daily_' . $lang];

            $subject = !empty(trim($subject))
                ? $subject
                : self::DEFAULT_DAILY_MAIL_SUBJECT;
        } else {
            $subject = isset($options['mdirector_subject_type_weekly']) &&
            ($options['mdirector_subject_type_weekly'] === self::DYNAMIC_SUBJECT)
                ? $options['mdirector_subject_dynamic_prefix_weekly_' . $lang]
                . ' ' .
                $this->getDynamicPostTitle(
                    $posts,
                    $options['mdirector_subject_dynamic_value_weekly']
                )
                : $options['mdirector_subject_weekly_' . $lang];

            $subject = !empty(trim($subject))
                ? $subject
                : self::DEFAULT_WEEKLY_MAIL_SUBJECT;
        }

        return $subject;
    }

    private function getDynamicPostTitle($posts, $criteria)
    {
        $titles = array_column($posts, 'title');
        $titlesSorted = ($criteria === self::DYNAMIC_CRITERIA_FIRST_POST)
            ? array_reverse($titles)
            : $titles;

        return reset($titlesSorted);
    }

    public function getTemplatePath($template)
    {
        if ($this->isTemplateCustom($template)) {
            $customTemplatesPath = $this->getCustomTemplatesPath($template);
            return $customTemplatesPath . $template . DIRECTORY_SEPARATOR;
        }

        return MDIRECTOR_TEMPLATES_PATH . $template . DIRECTORY_SEPARATOR;
    }

    public function isTemplateCustom($templateName)
    {
        return !in_array($templateName, $this->getDefaultTemplates());
    }

    private function getCustomTemplatesPath($template)
    {
        $childThemeTemplatesPath = $this->getCustomTemplatesOnChildThemePath();

        if (file_exists($childThemeTemplatesPath . $template)) {
            return $childThemeTemplatesPath;
        }

        return $this->getCustomTemplatesOnParentThemePath();
    }

    public function getTemplateURL($template)
    {
        if ($this->isTemplateCustom($template)) {
            return $this->getCustomTemplatesURI($template) . $template . DIRECTORY_SEPARATOR;
        }

        return plugin_dir_url(__DIR__) . 'templates' . DIRECTORY_SEPARATOR . $template . DIRECTORY_SEPARATOR;
    }

    private function getCustomTemplatesURI($template)
    {
        $childThemeTemplatesPath = $this->getCustomTemplatesOnChildThemePath();

        if (file_exists($childThemeTemplatesPath . $template)) {
            return get_stylesheet_directory_uri()
                . DIRECTORY_SEPARATOR
                . MDIRECTOR_NEWSLETTER
                . DIRECTORY_SEPARATOR;
        }

        return get_template_directory_uri()
            . DIRECTORY_SEPARATOR
            . MDIRECTOR_NEWSLETTER
            . DIRECTORY_SEPARATOR;
    }

    private function getTemplateMainFile($templatePath)
    {
        $templateHTML = $templatePath . self::TEMPLATE_HTML_BASE_FILE;
        $templateTwig = $templatePath . Mdirector_Newsletter_Twig::USER_TEMPLATE;

        if (file_exists($templateHTML)) {
            return self::TEMPLATE_HTML_BASE_FILE;
        } elseif (file_exists($templateTwig)) {
            return Mdirector_Newsletter_Twig::USER_TEMPLATE;
        }

        return false;
    }

    private function templateMainFileIsTwig($templateMainFile)
    {
        $fileParts = pathinfo($templateMainFile);

        return $fileParts['extension'] === 'twig';
    }

    /**
     * @param $template_data
     *
     * @return string
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    private function parsingTemplate($template_data)
    {
        $template_data['list'] = implode(
            '',
            array_map([$this, 'buildListFromPosts'], $template_data['posts'])
        );

        return $this->twigUserTemplate->render(
            self::TEMPLATE_HTML_BASE_FILE,
            $template_data
        );
    }

    private function sendMailAPI(
        $mail_content,
        $mail_subject,
        $isTestMode = false,
        $frequency = null,
        $lang = self::MDIRECTOR_DEFAULT_USER_LANG,
        $campaign = null,
        $lists = [],
        $segments = [],
        $groups = [],
        $from = null,
        $delivery = null
    ) {
        $options = $this->getPluginOptions();
        $MDirectorActive = get_option('mdirector_active');
        
        if ($MDirectorActive == self::SETTINGS_OPTION_ON) {
            $MDirectorNewsletterApi = new Mdirector_Newsletter_Api();

            // ========================================
            // 1. OBTENER CAMPAIGN ID SELECCIONADO
            // ========================================
            if ($campaign) {
                $campaignId = $campaign;
            } else {
                $campaignId = $this->getDeliveryCampaignId($frequency, $lang);
            }
            
            // Validar que tengamos un campaign ID
            if (empty($campaignId)) {
                $this->log(
                    'No campaign ID selected for delivery',
                    ['frequency' => $frequency, 'lang' => $lang],
                    self::LOG_ERROR
                );
                return false;
            }

            // ========================================
            // 2. OBTENER SENDER ID SELECCIONADO
            // ========================================
            if ($from) {
                $senderId = $from;
            } else {
                // Obtener del dropdown de senders según frecuencia
                $senderFieldName = 'mdirector_sender_' . $frequency;
                $senderId = isset($options[$senderFieldName]) ? intval($options[$senderFieldName]) : null;
            }
            
            // Si no hay sender seleccionado, registrar error
            if (empty($senderId)) {
                $this->log(
                    'No sender ID selected for delivery',
                    ['frequency' => $frequency, 'senderField' => $senderFieldName],
                    self::LOG_ERROR
                );
                return false;
            }

            // ========================================
            // 3. OBTENER GRUPOS/LISTAS
            // ========================================
            if (empty($lists) && empty($segments) && empty($groups)) {
                $lists = $this->getCurrentListsIds($frequency, $lang, $isTestMode);
                $groups = $this->getCurrentGroupsIds($frequency, $lang, $isTestMode);
                $segments = $this->getCurrentSegmentsIds($frequency, $lang, $isTestMode);
            }

            if (empty($lists) && empty($segments) && empty($groups)) {
                $this->log(
                    'No recipients (lists/segments/groups) configured for delivery',
                    ['frequency' => $frequency, 'lang' => $lang],
                    self::LOG_ERROR
                );
                return false;
            }

            $segments = $this->buildUserListsForCampaign($lists, $segments, $groups);

            // ========================================
            // 4. CONSTRUIR PAYLOAD PARA LA API
            // ========================================
            $APIData = [
                'campaignId' => intval($campaignId),  // Usar campaña seleccionada
                'actionName' => $delivery ?: ($frequency . '_' . gmdate('Y_m_d_H_i_s')),
                'elementType' => 1,                                
                'isConfirmable' => true,
                'sender' => [
                    'senderId' => intval($senderId),  // Usar sender seleccionado
                    'subject' => $mail_subject
                ],
                'groups' =>  json_decode($segments), 
                'message' => [
                    'contentHtml' => $mail_content
                ],
                'date' => [
                    'isImmediateShipment' => true,
                    'reinforcement' => [                        
                        'isReinforcementShipment' => false,                        
                    ]
                ],
            ];     
            
            // ========================================
            // 5. ENVIAR A LA API
            // ========================================
            $token = $options['mdirector_bearer_token'];
            $apiResponse = $MDirectorNewsletterApi->callAPI(
                $token,
                self::MDIRECTOR_API_DELIVERY_ENDPOINT,
                'POST',
                $APIData
            );

            if (is_wp_error($apiResponse)) {
                // No incluir contentHtml en logs (demasiado grande)
                $logData = $APIData;
                unset($logData['message']);
                
                $this->log(
                    'Error calling API: ' . $apiResponse->get_error_message(),
                    [
                        'APIData' => $logData,
                        'error_code' => $apiResponse->get_error_code()
                    ],
                    self::LOG_ERROR
                );
                return false;
            }

            $MDirectorSendResp = json_decode($apiResponse);

            
            if (!$MDirectorSendResp || !isset($MDirectorSendResp->actionId)) {
                $this->log(
                    'Invalid API response',
                    [
                        'raw_response' => substr($apiResponse, 0, 500), // Solo primeros 500 chars
                        'decoded' => $MDirectorSendResp
                    ],
                    self::LOG_ERROR
                );
                return false;
            }

            // Log exitoso (sin incluir el HTML completo del mensaje)
            $this->log(
                'Email successfully sent to API',
                [
                    'campaignId' => $campaignId,
                    'senderId' => $senderId,
                    'actionId' => $MDirectorSendResp->actionId,
                    'frequency' => $frequency,
                    'lang' => $lang,
                    'message' => $MDirectorSendResp->message ?? ''
                ],
                self::LOG_INFO
            );            

            return true;
        }

        return false;
    }

    private function getDeliveryCampaignId(
        $frequency,
        $lang = self::MDIRECTOR_DEFAULT_USER_LANG
    ) {
        $options = $this->getPluginOptions();

        return ($frequency === self::DAILY_FREQUENCY)
            ? $options[self::MDIRECTOR_DAILY_CAMPAIGN . $lang]
            : $options[self::MDIRECTOR_WEEKLY_CAMPAIGN . $lang];
    }

    public function getCurrentListsIds($type, $lang, $isTestMode = false)
    {
        $options = $this->getPluginOptions();

        if ($this->hasToUseTestList($options, $isTestMode)) {
            return isset($options['mdirector_' . $type . '_list_test_' . $lang])
                ? $options['mdirector_' . $type . '_list_test_' . $lang] : [];
        }

        return isset($options['mdirector_' . $type . '_list_' . $lang])
            ? $options['mdirector_' . $type . '_list_' . $lang] : [];
    }

    private function getCurrentGroupsIds($type, $lang, $isTestMode = false)
    {
        $options = $this->getPluginOptions();

        if ($this->hasToUseTestList($options, $isTestMode)) {
            return isset(
                $options['mdirector_' . $type . '_list_test_group_'
                . $lang]
            ) ? $options['mdirector_' . $type . '_list_test_group_'
            . $lang] : [];
        }

        return isset($options['mdirector_' . $type . '_list_group_' . $lang])
            ? $options['mdirector_' . $type . '_list_group_' . $lang] : [];
    }

    private function getCurrentSegmentsIds($type, $lang, $isTestMode = false)
    {
        $options = $this->getPluginOptions();

        if ($this->hasToUseTestList($options, $isTestMode)) {
            return isset(
                $options['mdirector_' . $type . '_list_test_segment_'
                . $lang]
            ) ? $options['mdirector_' . $type
            . '_list_test_segment_' . $lang] : [];
        }

        return isset($options['mdirector_' . $type . '_list_segment_' . $lang])
            ? $options['mdirector_' . $type . '_list_segment_' . $lang] : [];
    }

    private function buildUserListsForCampaign($lists, $segments, $groups)
    {
        return json_encode(
            array_merge(
                $this->parseUserLists($lists),
                $this->parseUserSegments($segments),
                $this->parseUserGroups($groups)
            )
        );
    }

    private function parseUserLists($lists)
    {
        return (is_array($lists))
            ? array_map([$this, 'buildUserList'], $lists)
            : (!empty($lists)
                ? [self::API_LIST_KEYWORD . $lists]
                : []);
    }

    private function parseUserSegments($lists)
    {
        return (is_array($lists))
            ? $lists
            : (!empty($lists)
                ? [$lists]
                : []);
    }

    private function parseUserGroups($lists)
    {
        return (is_array($lists))
            ? array_map([$this, 'buildUserGroupSegments'], $lists)
            : (!empty($lists)
                ? [self::API_GROUP_KEYWORD . $lists]
                : []);
    }

    public function resetNewsletterDeliveryDateSent($frequency)
    {
        $options = $this->getPluginOptions();
        $process = ($frequency === self::DAILY_FREQUENCY)
            ? 'mdirector_daily_sent'
            : 'mdirector_weekly_sent';

        $options[$process] = null;

        $this->log(
            'Reset newsletter delivery date sent...',
            ['frequency' => $frequency],
            self::LOG_INFO
        );
        $this->updateMDirectorSettingsWithLog($options, 'Reset newsletter delivery date sent...');

        wp_reset_postdata();
    }

    /**
     * @param $previewMode
     * @param $isTestMode
     * @param $forceRealDeliveryMode
     *
     * @return array
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    public function buildWeeklyMails($previewMode = false, $isTestMode = false, $forceRealDeliveryMode = false)
    {
        $response = [];
        $delivery = 0;
        $isAlreadySentInOtherLanguage = false;

        foreach ($this->getCurrentLanguages() as $lang) {
            $lang = $lang['code'];

            if ($this->isDeliveryActive($lang, self::WEEKLY_FREQUENCY, $isTestMode)) {
                if ($delivery > 0) {
                    $isAlreadySentInOtherLanguage = true;
                }

                $response[$lang] = $this->mdSendWeeklyMails(
                    $lang, $previewMode, $isAlreadySentInOtherLanguage, $isTestMode, $forceRealDeliveryMode
                );

                // TODO: This solution is not accurate
                if (!$response[$lang]) {
                    break;
                }

                $delivery++;
            }
        }

        return $response;
    }

    /**
     * @param $lang
     * @param $previewMode
     * @param $isAlreadySentInOtherLanguage
     * @param $isTestMode
     * @param $forceRealDeliveryMode
     *
     * @return bool
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    private function mdSendWeeklyMails(
        $lang,
        $previewMode,
        $isAlreadySentInOtherLanguage = false,
        $isTestMode = false,
        $forceRealDeliveryMode = false
    ) {
        $options = $this->getPluginOptions();

        $deliveryScheduleDayOfWeek = $options['mdirector_frequency_day']
            ?: '1'; # Default: Monday
        $currentDayOfWeek = gmdate('N');
        $hour = $options['mdirector_hour_weekly']
            ?: self::MIDNIGHT_HOUR;
        $timeExploded = explode(':', $hour);
        $actualTime = time();
        $mailSent = gmdate(
            'Y-m-d', strtotime(
                isset($options['mdirector_weekly_sent'])
                    ? $options['mdirector_weekly_sent'] : null
            )
        );
        $canSend = $mailSent !== gmdate('Y-m-d');

        $this->log(
            'mdSend Weekly Mails',
            [
                'previewMode' => $previewMode,
                'isTestMode' => $isTestMode,
                'isAlreadySentInOtherLanguage' => $isAlreadySentInOtherLanguage,
                'forceRealDeliveryMode' => $forceRealDeliveryMode,
                'canSend' => $canSend
            ],
            self::LOG_INFO
        );

        $toDate = $this->calculateToDate($timeExploded);

        if ($previewMode ||
            $forceRealDeliveryMode ||
            $isTestMode ||
            $isAlreadySentInOtherLanguage ||
            ($canSend && $currentDayOfWeek === $deliveryScheduleDayOfWeek && ($actualTime >= strtotime($toDate)))
        ) {
            $fromDate =
                $this->calculateFromDate($timeExploded, self::WEEKLY_FREQUENCY);
            $args = $this->getPostsArgs($fromDate, $toDate);

            if (!empty($excludeCats = $this->getExcludeCats())) {
                $args['cat'] = implode(', ', $excludeCats);
            }

            $posts = $this->buildPosts($this->getPosts($args, $lang), $lang);

            if (!empty($posts)) {
                !$previewMode && !$isTestMode && $this->cleanNewsletterProcess(self::WEEKLY_FREQUENCY);
                if ($this->mdSendMail($posts, self::WEEKLY_FREQUENCY, $lang, $previewMode, $isTestMode)) {
                    return true;
                }

                // Process failed; reset last time sent...
                $this->log('Weekly delivery failed...', [], self::LOG_ERROR);

                $this->resetNewsletterDeliveryDateSent(self::WEEKLY_FREQUENCY);
                return false;
            }

            $this->log(
                'Weekly delivery aborted: there are no new posts for daily mails.',
                [],
                self::LOG_ERROR
            );

            $this->log(
                'There are no new posts for weekly mails and lang ' . $lang,
                $args,
                self::LOG_NOTICE
            );
        } else {
            $this->log(
                "Weekly delivery can not be sent (last delivery was $mailSent)",
                [
                    'Current date' => gmdate('l H:i'),
                    'Next delivery schedule' => jddayofweek($deliveryScheduleDayOfWeek - 1, 1) . ' ' . $hour,
                    'Current day of week' => $currentDayOfWeek,
                    'Delivery schedule day of week' => $deliveryScheduleDayOfWeek,
                    'actualTime' => $actualTime,
                    'strTime' => strtotime($toDate),
                    'canSend' => $canSend
                ],
                self::LOG_NOTICE
            );
        }

        return false;
    }

    public function getDefaultUserLists($frequency, $lang, $isTestMode = false)
    {
        return [
            self::USER_LIST => $this->getCurrentListsIds($frequency, $lang, $isTestMode),
            self::USER_GROUP => $this->getCurrentGroupsIds($frequency, $lang, $isTestMode),
            self::USER_SEGMENT => $this->getCurrentSegmentsIds($frequency, $lang, $isTestMode),
        ];

    }

    public function prettyPrintMultiArray($arr)
    {
        $arr = json_encode($arr);
        $arr = str_replace('[]', ' none', $arr);
        $arr = str_replace(['{', '}', '[', ']'], '', $arr);
        $arr = str_replace(',', ', ', $arr);

        return str_replace(':', ': ', $arr);
    }

    public function reportNotice($template, $templateData)
    {
        $this->pluginNotices[] =
            $this->adminTemplate->renderBlock(
                $template,
                $templateData
            );
    }

    public function printNotices()
    {
        if (count($this->pluginNotices)) {
            echo wp_kses( join(' ', $this->pluginNotices), self::allowedHtml() );
        }
    }

    public function bootNotices()
    {
        add_action('admin_notices', [$this, 'printNotices']);
    }

    /**
     * @param $post
     *
     * @return string
     * @throws \Twig\Error\LoaderError
     * @throws \Twig\Error\RuntimeError
     * @throws \Twig\Error\SyntaxError
     */
    private function buildListFromPosts($post)
    {
        $postData = [
            'post_image' => $this->getImageTagFromPost($post),
            'postImage' => $this->getImageTagFromPost($post),
            'titleURL' => $post['link'],
            'title' => $post['title'],
            'content' => $post['excerpt']
        ];

        return $this->twigUserTemplate->render('list.html', $postData);
    }

    private function getImageTagFromPost($post)
    {
        if (!$post['post_image']) {
            return '';
        }

        $templateData = [
            'post' => $post
        ];

        return $this->adminTemplate->renderBlock(
            'imageTagFromPost',
            $templateData
        );
    }

    private function getSegmentId($segment)
    {
        return $segment->segId;
    }

    private function buildUserList($list)
    {
        return ["id" => (int) $list,"type" => 2];
    }

    private function buildUserGroupSegments($group)
    {
        return self::API_GROUP_KEYWORD . $group;
    }

    private function setHTMLContentType()
    {
        return 'text/html';
    }

    private function parsePost($post)
    {
        return [
            'ID' => $post->ID,
            'title' => $post->post_title,
            'content' => $post->post_content,
            'link' => get_permalink($post->ID),
            'truncate_content' => $this->textTruncate($post->post_content),
            'excerpt' => $post->post_excerpt ?: $this->textTruncate($post->post_content),
            'date' => $post->post_date,
            'post_image' => $this->getMainImage($post->ID, 'thumb'),
            'post_image_size' => $this->getMainImageSize()
        ];
    }

    private function textTruncate($string)
    {
        $string = wp_strip_all_tags($string);
        $string = preg_replace('|\[(.+?)\](.+?\[/\\1\])?|s', '', $string);

        if (preg_match('/<!--more(.*?)?-->/', $string, $matches)) {
            list($main) = explode($matches[0], $string, 2);

            return $main;
        } else {
            $string = htmlspecialchars($string);


            if (strlen($string) > self::EXCERPT_LENGTH_CHARS) {
                $string =
                    preg_replace(
                        '/\s+?(\S+)?$/', '...',
                        substr($string, 0, self::EXCERPT_LENGTH_CHARS)
                    );
            }

            return $string;
        }
    }

    private function getMainImage($postId, $size)
    {
        if ($postId) {
            if (has_post_thumbnail($postId)) {
                $postThumbnailId = get_post_thumbnail_id($postId);
                $thumbnail =
                    wp_get_attachment_image_src($postThumbnailId, $size);

                return $thumbnail[0];
            }
        }

        return false;
    }

    private function getMainImageSize()
    {
        return (self::TEMPLATES_PATH === 'templates-mdirector/')
            ? self::MAX_IMAGE_SIZE_MDIRECTOR_TEMPLATE
            : self::MAX_IMAGE_SIZE_DEFAULT_TEMPLATE;
    }
}
