<?php

namespace SEOAIC\posts_mass_actions;

use Exception;
use SEOAIC\helpers\HTMLBuilers;
use SEOAIC\helpers\SEOPluginsHelper;
use SEOAIC\patterns\LeadsFormPattern;
use SEOAIC\SEOAIC_SETTINGS;
use SEOAIC\thirdparty_plugins_meta_tags\YoastMetaTags;
use SEOAIC\traits\Debug;
use WP_REST_Request;

class LeadsFormGenerate extends AbstractPostsMassAction
{
    use Debug;

    private const PER_REQUEST__UPDATES_CONTENT = 5;
    private const PENDING_STATUS = 'pending';
    private const FAILED_STATUS = 'failed';
    private const COMPLETED_STATUS = 'completed';
    public const PRIMARY_GOAL_OPTIONS = [
        'lead_qualification'    => 'Lead Qualification',
        'service_inquiry'       => 'Service Inquiry',
        'custom_interaction'    => 'Custom Interaction',
    ];

    public function __construct($seoaic)
    {
        parent::__construct($seoaic);

        $this->backendActionURL = 'api/lead/post-form/generate';
        $this->backendCheckStatusURL = 'api/lead/post-form/generate/status';
        $this->backendContentURL = 'api/lead/post-form/generate/content';
        $this->backendClearURL = 'api/lead/post-form/generate/clear';
        $this->statusField = 'seoaic_leads_form_generate_status';
        $this->cronCheckStatusHookName = 'seoaic/leads_form/generate/check_status_cron_hook';

        $this->successfullRunMessage = esc_html__('Generation started', 'seoaic');
        $this->completeMessage = esc_html__('All posts have been updated with Leads Form.', 'seoaic');
        $this->stopMessage = esc_html__('Lead forms generation have been stopped.', 'seoaic');
    }

    public static function init()
    {
        // see SEOAIC_POSTS __construct function
        global $SEOAIC;

        $self = new self($SEOAIC);
        add_action($self->cronCheckStatusHookName, [$self, 'cronPostsCheckStatus']);
    }

    public static function commonFormElements($formId = '', $hideElements = false): void
    {
        ?>
        <div class="seoaic-generate-leads-form-wrapper <?php echo $hideElements ? 'display-none' : '';?>">
            <div class="mb-10">
                <label class="mb-10"><?php esc_html_e('Leads Form primary goal', 'seoaic');?></label>
                <select
                    name="leads_form_goal"
                    id="seoaic_leads_form_goal"
                    class="seoaic-form-item form-select"
                    form="<?php echo !empty($formId) ? esc_attr($formId) : '';?>"
                >
                    <?php
                    echo wp_kses(HTMLBuilers::options(self::PRIMARY_GOAL_OPTIONS), [
                        'option' => [
                            'value'     => [],
                            'selected'  => [],
                            'class'     => [],
                        ],
                    ]);
                    ?>
                </select>
            </div>

            <div class="seoaic-popup__field seoaic-generate-leads-form-prompt-wrapper mb-10 display-none">
                <label class="mb-10 mt-20"><?php esc_html_e('Leads Form goal prompt', 'seoaic');?></label>
                <textarea
                    name="leads_form_prompt"
                    class="seoaic-form-item mt-0 seoaic-textarea-autoresize"
                ><?php esc_html_e(SEOAIC_SETTINGS::getLeadsFormGoalPromptDefault());?></textarea>
            </div>

            <div id="seoiac-modal-steps-range" class="seoaic-settings-range seoaic-mb-15 mt-15" data-min="1" data-max="15" data-step="1">
                <label><?php esc_html_e('Number of steps (range):', 'seoaic');?>
                    <span class="range-min">5</span> -
                    <span class="range-max">11</span>
                    <input type="hidden"
                        name="leads_form_steps_range_min"
                        value="5"
                        form="post-mass-creation-form"
                        id="seoaic_modal_steps_range_min"
                        class="seoaic-settings-range-min seoaic-form-item"
                    >
                    <input type="hidden"
                        name="leads_form_steps_range_max"
                        value="11"
                        form="post-mass-creation-form"
                        id="seoaic_modal_steps_range_max"
                        class="seoaic-settings-range-max seoaic-form-item"
                    >
                </label>
                <div id="seoiac-modal-steps-range-slider" class="seoaic-settings-range-slider"></div>
            </div>
        </div>
        <?php
    }

    public static function formElements($formId='', $prefix_id = '', $select_class = ''): string
    {
        ob_start();
        ?>
        <div class="mb-10">
            <label class="seoaic-cursor-pointer">
                <input type="checkbox"
                    id="seoaic_generate_leads_form<?php esc_attr_e($prefix_id);?>"
                    class="seoaic-form-item"
                    name="generate_leads_form"
                    form="<?php echo !empty($formId) ? esc_attr($formId) : '';?>"
                >
                <?php esc_html_e('Generate Leads Form', 'seoaic');?>
            </label>
        </div>

        <div class="seoaic-generate-leads-form-wrapper display-none">
            <div class="mb-10">
                <label class="mb-10"><?php esc_html_e('Leads Form primary goal', 'seoaic');?></label>
                <select
                    name="leads_form_goal"
                    id="seoaic_leads_form_goal<?php esc_attr_e($prefix_id);?>"
                    class="seoaic-form-item form-select <?php esc_attr_e($select_class);?>"
                    form="<?php echo !empty($formId) ? esc_attr($formId) : '';?>"
                >
                    <?php
                    echo wp_kses(HTMLBuilers::options(self::PRIMARY_GOAL_OPTIONS), [
                        'option' => [
                            'value'     => [],
                            'selected'  => [],
                            'class'     => [],
                        ],
                    ]);
                    ?>
                </select>
            </div>

            <div class="seoaic-popup__field seoaic-generate-leads-form-prompt-wrapper display-none">
                <label class="mb-10 mt-20"><?php esc_html_e('Leads Form goal prompt', 'seoaic');?></label>
                <textarea
                    name="leads_form_prompt"
                    class="seoaic-form-item mt-0 seoaic-textarea-autoresize"
                ><?php esc_html_e(SEOAIC_SETTINGS::getLeadsFormGoalPromptDefault());?></textarea>
            </div>
        </div>
        <?php
        $html = ob_get_clean();

        return $html;
    }

    public static function generatePostFormElements($formId = '', $hideElements = false): void
    {
        ?>
        <div class="mb-10">
            <label class="seoaic-cursor-pointer">
                <input type="checkbox"
                    id="seoaic_generate_leads_form"
                    class="seoaic-form-item"
                    name="generate_leads_form"
                    form="<?php echo !empty($formId) ? esc_attr($formId) : '';?>"
                >
                <?php esc_html_e('Generate Leads Form', 'seoaic');?>
            </label>
        </div>
        <?php
        self::commonFormElements($formId, $hideElements);
    }

    public static function generateLeadFormsFormElements($formId = '', $hideElements = false): void
    {
        $getNames = function ($array) {
            return array_map(function ($item) {
                return $item['name'];
            }, $array);
        };

        $services = $getNames(SEOAIC_SETTINGS::getCompanyServices());
        $target_audience = $getNames(SEOAIC_SETTINGS::getTargetAudience());
        ?>
        <div class="mb-10">
            <label class="mb-10"><?php esc_html_e('Company services', 'seoaic');?></label>
            <select
                name="company_service"
                class="seoaic-form-item form-select"
                form="<?php echo !empty($formId) ? esc_attr($formId) : '';?>"
            >
                <?php
                echo wp_kses(HTMLBuilers::options($services), [
                    'option' => [
                        'value'     => [],
                        'selected'  => [],
                        'class'     => [],
                    ],
                ]);
                ?>
            </select>
        </div>

        <div class="mb-10">
            <label class="mb-10"><?php esc_html_e('Target audience', 'seoaic');?></label>
            <select
                name="target_audience"
                class="seoaic-form-item form-select"
                form="<?php echo !empty($formId) ? esc_attr($formId) : '';?>"
            >
                <?php
                echo wp_kses(HTMLBuilers::options($target_audience), [
                    'option' => [
                        'value'     => [],
                        'selected'  => [],
                        'class'     => [],
                    ],
                ]);
                ?>
            </select>
        </div>
        <?php
        self::commonFormElements($formId, $hideElements);
    }

    public function prepareData($request)
    {
        $postIDs = [];

        if (is_array($request['post-mass-edit'])) {
            $postIDs = $request['post-mass-edit'];
        } else {
            $postIDs = [$request['post-mass-edit']];
        }

        // make sure posts are available
        $posts = $this->getAvailablePostsForLeadsGeneration($postIDs);

        if (empty($posts)) {
            throw new Exception('Posts not found');
        }

        $postsData = [];
        $seoPlugin = SEOPluginsHelper::getAvailableSEOPlugin();
        if (!$seoPlugin) { // a fallback if no plugins available
            $seoPlugin = new YoastMetaTags();
        }

        foreach ($posts as $post) {
            $postLanguage = $this->seoaic->multilang->get_post_language($post->ID);
            $postLanguage = $postLanguage ? $postLanguage : 'English';

            $postsData[] = [
                'id'                => $post->ID,
                'primary_keyword'   => $seoPlugin->getKeyword($post->ID),
                'topic'             => $post->post_title,
                'content'           => $post->post_content,
                'language'          => $postLanguage,
            ];
        }

        $companyServiceIndex = !empty($request['company_service']) ? $request['company_service'] : [];
        $targetAudienceIndex = !empty($request['target_audience']) ? $request['target_audience'] : [];
        $companyService     = isset($companyServiceIndex) ? SEOAIC_SETTINGS::getSelectedRepeaterValues('seoaic_services', $companyServiceIndex, true) : '';
        $targetAudience     = isset($targetAudienceIndex) ? SEOAIC_SETTINGS::getSelectedRepeaterValues('seoaic_target_audience', $targetAudienceIndex, true) : '';
        $primaryGoal        = $request['leads_form_goal'];
        $stepsMin           = !empty($request['leads_form_steps_range_min']) ? intval($request['leads_form_steps_range_min']) : 5;
        $stepsMax           = !empty($request['leads_form_steps_range_max']) ? intval($request['leads_form_steps_range_max']) : 11;
        $prompt             = sanitize_textarea_field($request['leads_form_prompt']);

        $data = [
            'post_data'     => $postsData,
            'company_data'  => [
                "description"       => SEOAIC_SETTINGS::getBusinessDescription(),
                "service_details"   => $companyService,
                "target_audience"   => $targetAudience,
            ],
            'specific_data' => [
                "primary_goal"      => $primaryGoal,
                "custom_prompt"     => $prompt,
                "steps_number_min"  => $stepsMin,
                "steps_number_max"  => $stepsMax,
            ],
        ];

        return $data;
    }

    public function prepareDataFromAPIRequest(WP_REST_Request $request)
    {
        $postID             = intval($request->get_param('post_id'));
        $companyServiceIndex = $request->get_param('company_service');
        $companyServiceIndex = !empty($companyServiceIndex) ? $companyServiceIndex : [];
        $targetAudienceIndex = $request->get_param('target_audience');
        $targetAudienceIndex = !empty($targetAudienceIndex) ? $targetAudienceIndex : [];
        $companyService     = isset($companyServiceIndex) ? SEOAIC_SETTINGS::getSelectedRepeaterValues('seoaic_services', $companyServiceIndex, true) : '';
        $targetAudience     = isset($targetAudienceIndex) ? SEOAIC_SETTINGS::getSelectedRepeaterValues('seoaic_target_audience', $targetAudienceIndex, true) : '';
        $primaryGoal        = sanitize_text_field($request->get_param('primary_goal'));
        $stepsMin           = intval($request->get_param('range_min'));
        $stepsMax           = intval($request->get_param('range_max'));
        $prompt             = sanitize_textarea_field($request->get_param('prompt'));

        // make sure posts are available
        $posts = $this->getAvailablePostsForLeadsGeneration([$postID]);

        if (empty($posts)) {
            throw new Exception('Posts not found');
        }

        $postsData = [];
        $seoPlugin = SEOPluginsHelper::getAvailableSEOPlugin();
        if (!$seoPlugin) { // a fallback if no plugins available
            $seoPlugin = new YoastMetaTags();
        }

        foreach ($posts as $post) {
            $postLanguage = $this->seoaic->multilang->get_post_language($post->ID);
            $postLanguage = $postLanguage ? $postLanguage : 'English';

            $postsData[] = [
                'id'                => $post->ID,
                'primary_keyword'   => $seoPlugin->getKeyword($post->ID),
                'topic'             => $post->post_title,
                'content'           => $post->post_content,
                'language'          => $postLanguage,
            ];
        }

        $data = [
            'post_data'     => $postsData,
            'company_data'  => [
                "description"       => SEOAIC_SETTINGS::getBusinessDescription(),
                "service_details"   => $companyService,
                "target_audience"   => $targetAudience,
            ],
            'specific_data' => [
                "primary_goal"      => $primaryGoal,
                "custom_prompt"     => $prompt,
                "steps_number_min"  => $stepsMin,
                "steps_number_max"  => $stepsMax,
            ],
        ];

        return $data;
    }

    /**
     * Override parent's method to be able to make requests in a loop
     */
    public function sendActionRequest($data = [])
    {
        $this->debugLog(__CLASS__, 'Data: ',  $data);
        $result = $this->sendRequest($this->getBackendActionURL(), $data);
        $result['posts_data'] = $data['post_data'];
        $this->debugLog(__CLASS__, 'Response: ',  $result);

        return $result;
    }

    public function processActionResults($result = [])
    {
        if (
            !empty($result['status'])
            && 'success' == $result['status']
            && !empty($result['posts_data'])
        ) {
            foreach ($result['posts_data'] as $postData) {
                $this->updatePostData($postData['id'], [
                    $this->getStatusField() => self::PENDING_STATUS,
                ]);
            }

        } else {
            if (!empty($result['message'])) {
                $this->errors[] = $result['message'];
            }

            foreach ($result['posts_data'] as $postData) {
                $this->updatePostData($postData['id'], [
                    $this->getStatusField() => '',
                ]);
            }
        }

        if ($this->useCron) {
            $this->registerPostsCheckStatusCron();
        }

        return true;
    }

    public function pocessCheckStatusResults($result)
    {
        $returnData = [
            'done' => [],
            'failed' => [],
        ];


        if (!empty($result)) {
            if (
                !empty($result['completed'])
                && is_array($result['completed'])
            ) {
                $returnData['done'] = $this->processCompleted($result['completed']);
            }

            if (!empty($result['failed'])) {
                $this->processFailed($result['failed']);
                $returnData['failed'] = array_merge($returnData['failed'], $result['failed']);
            }
        }

        return $returnData;
    }

    protected function processCompleted($ids = [])
    {
        if (
            empty($ids)
            || !is_array($ids)
        ) {
            return;
        }

        // doublecheck the IDs to exist
        $posts = $this->getGeneratingPostsWithLeadsByIDs($ids, false, true);
        if (empty($posts)) {
            return;
        }

        $postsByIDs = array_combine(array_column($posts, 'ID'), $posts);

        // change status
        foreach ($postsByIDs as $origPost) {
            $this->updatePostData($origPost->ID, [
                $this->getStatusField() => 'completed',
            ]);
        }

        $postIDs = array_map(function ($item) {
            return $item->ID;
        }, $posts);

        $data = [
            'post_ids' => $postIDs,
        ];
        $return = [];
        $contentResult = $this->sendContentRequest($data);

        if (
            !empty($contentResult)
            && is_array($contentResult)
        ) {
            foreach ($contentResult as $item) {
                if (
                    empty($item)
                    || empty($item['postId'])
                    || empty($item['content'])
                    || !in_array($item['postId'], $postIDs)
                ) {
                    continue;
                }
                $formContent = '';
                $itemContent = [];

                // save pattern
                if (!empty($item['content'])) {
                    $name = $item['content'][0]['question'];

                    $itemContent = $this->seoaic->leadsAddon->makeProperStepsData($item['content']);
                    $formContent = $this->seoaic->leadsAddon->makeShortcodeFromAIResponse($itemContent);
                    (new LeadsFormPattern($name, $formContent))->save();

                    // save in content only for for ajax requests, not for API ones
                    if (!$this->isAPI) {
                        $postContent = $postsByIDs[$item['postId']]->post_content;
                        wp_update_post([
                            "ID"            => $item['postId'],
                            "post_content"  => $postContent . '<br>' . $formContent,
                        ]);
                    }
                }

                $return[$item['postId']] = [
                    'id'        => $item['postId'],
                    'content'   => $itemContent,
                ];
            }
        }

        return $return;
    }

    protected function processFailed($ids = [])
    {
        if (
            !empty($ids)
            && is_array($ids)
        ) {
            // doublecheck the IDs to exist
            $posts = $this->getGeneratingPostsWithLeadsByIDs($ids, false, true);
            if (empty($posts)) {
                return;
            }

            $postsByIDs = array_combine(array_column($posts, 'ID'), $posts);

            // change status
            foreach ($postsByIDs as $origPost) {
                $this->updatePostData($origPost->ID, [
                    $this->getStatusField() => self::FAILED_STATUS,
                ]);
            }
        }
    }

    public function isRunning()
    {
        $posts = $this->getGeneratingPostsWithLeadsAll();

        return !empty($posts);
    }

    public function cronPostsCheckStatus()
    {
        $this->debugLog('[CRON]', __CLASS__);
        $this->getStatusResults();
    }

    public function stop()
    {
        $this->seoaic->posts->debugLog();
        $posts = $this->getGeneratingPostsWithLeadsAll();

        if (!empty($posts)) {
            foreach ($posts as $post) {
                $this->updatePostData($post->ID, [
                    $this->getStatusField() => '',
                ]);
            }
        }

        $this->sendClearRequest(['full' => true]);
        $this->unregisterPostsCheckStatusCron();
    }


    /**
     * Gets available posts for Leads geneartion, e.g. not in "Pending" state
     * @param array $postIDs options array of IDs to search among
     * @return array
     */
    private function getAvailablePostsForLeadsGeneration($postIDs = [])
    {
        $args = [
            'posts_per_page'    => -1,
            'post_type'         => 'any',
            'post_status'       => 'any',
            'lang'              => '', // disable default lang setting
            'skip_hiding'       => true,
            'meta_query'        => [
                'relation' => 'OR',
                [
                    'key' => $this->getStatusField(),
                    'value' => self::PENDING_STATUS,
                    'compare' => '!=',
                ],
                [
                    'key' => $this->getStatusField(),
                    'compare' => 'NOT EXISTS',
                ],
            ],
        ];

        if (!empty($postIDs)) {
            $postIDs = !is_array($postIDs) ? [$postIDs] : $postIDs;
            $args['post__in'] = $postIDs;
        }
        $posts = get_posts($args);

        return $posts;
    }

    /**
     * Gets all posts that were sent for review
     * @param array $postIDs options array of IDs to search among
     * @return array
     */
    public function getGeneratingPostsWithLeadsAll()
    {
        $args = [
            'posts_per_page'    => -1,
            'post_type'         => 'any',
            'post_status'       => 'any',
            'lang'              => '', // disable default lang setting
            'skip_hiding'       => true,
            'meta_query'        => [
                'relation' => 'AND',
                [
                    'key' => $this->getStatusField(),
                    'value' => self::PENDING_STATUS,
                    'compare' => '=',
                ],
            ],
        ];

        $posts = get_posts($args);

        return $posts;
    }

    /**
     * @param array|int $ids Accepts array if IDs or single ID
     */
    private function getGeneratingPostsWithLeadsByIDs($ids = [], $returnIDsOnly = false, $all = false)
    {
        $args = [
            'numberposts'       => ($all ? -1 : self::PER_REQUEST__UPDATES_CONTENT),
            'post_type'         => 'any',
            'post_status'       => 'any',
            'lang'              => '', // disable default lang setting
            'skip_hiding'       => true,
            'meta_query'        => [
                'relation' => 'AND',
                [
                    'key' => $this->getStatusField(),
                    'value' => self::PENDING_STATUS,
                    'compare' => '=',
                ],
            ],
        ];

        if (!empty($ids)) {
            $args['include'] = !is_array($ids) ? [$ids] : $ids;
        }

        if ($returnIDsOnly) {
            $args['fields'] = 'ids';
        }

        $posts = get_posts($args);

        return $posts;
    }

    public function resetPostStatus(int $postID)
    {
        $this->updatePostData($postID, [
            $this->getStatusField() => '',
        ]);
    }
}
