<?php

namespace SEOAIC\posts_mass_actions;

use Exception;
use SEOAIC\helpers\ArrayHelpers;
use SEOAIC\helpers\ImageHelpers;
use SEOAIC\interfaces\PostsMassActionStoppable;
use SEOAIC\SEOAIC;
use SEOAIC\SEOAIC_POSTS;
use SEOAIC\SEOAIC_SETTINGS;
use SEOAIC_FRAMES;
use SEOAIC\traits\Debug;

class ThumbnailsGenerate extends AbstractPostsMassAction implements PostsMassActionStoppable
{
    use Debug;

    private const GENERATING_STATUS = 'pending';
    public const FAILED_STATUS = 'failed';
    private const COMPLETED_STATUS = 'completed';

    public const DEFAULT_THUMBNAIL_WIDTH = 1024;

    public const DEFAULT_THUMBNAIL_HEIGHT = 768;

    public const MIN_THUMBNAIL_WIDTH = 256;

    public const MIN_THUMBNAIL_HEIGHT = 256;

    public const MAX_THUMBNAIL_WIDTH = 1440;

    public const MAX_THUMBNAIL_HEIGHT = 1440;

    public const MASS_GENERATION_DATA = 'mass_generation_data';

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

        $this->backendActionURL = 'api/ai/posts/thumbnails/generate';
        $this->backendCheckStatusURL = 'api/ai/posts/thumbnails/generate/status';
        $this->backendContentURL = 'api/ai/posts/thumbnails/generate/content';
        $this->backendClearURL = 'api/ai/posts/thumbnails/generate/clear';
        $this->statusField = 'seoaic_thumbnail_generate_status';
        $this->cronCheckStatusHookName = 'seoaic/posts/thumbnails_generate/check_status_cron_hook';
        // $this->loader = new PostsEditLoader();

        $this->successfullRunMessage = 'Update started';
        $this->completeMessage = '';
        $this->stopMessage = 'Thumbnails generation have been stopped.';
    }

    private function getIDsFromRequest($data): array
    {
        $IDs = [];

        if (
            empty($data['ids']) // from mass generate
            && empty($data['post-mass-edit']) // from mass edit
        ) {
            throw new Exception('No posts selected');
        }

        if (!empty($data['ids'])) {
            $IDs = $data['ids'];
        } else {
            if (is_array($data['post-mass-edit'])) {
                $IDs = $data['post-mass-edit'];
            } else {
                $IDs = [$data['post-mass-edit']];
            }
        }

        return $IDs;
    }

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

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

    public function preRun($data) {
        $generateThumbnailStyle = !empty($data[SEOAIC_POSTS::GENERATE_THUMBNAIL_STYLE_FIELD])
            ? $data[SEOAIC_POSTS::GENERATE_THUMBNAIL_STYLE_FIELD]
            : SEOAIC_SETTINGS::getImageGenerateStyle();
        $prompt = !empty($data[SEOAIC_POSTS::GENERATE_THUMBNAIL_PROMPT_FIELD])
            ? $data[SEOAIC_POSTS::GENERATE_THUMBNAIL_PROMPT_FIELD]
            : SEOAIC_SETTINGS::getImageGeneratePromptDefault();
        $width = !empty($data[SEOAIC_POSTS::GENERATE_THUMBNAIL_WIDTH])
            ? $data[SEOAIC_POSTS::GENERATE_THUMBNAIL_WIDTH]
            : SEOAIC_SETTINGS::getImageGenerateWidthDefault();;
        $height = !empty($data[SEOAIC_POSTS::GENERATE_THUMBNAIL_HEIGHT])
            ? $data[SEOAIC_POSTS::GENERATE_THUMBNAIL_HEIGHT]
            : SEOAIC_SETTINGS::getImageGenerateHeightDefault();;
        $IDs = $this->getIDsFromRequest($data);

        foreach ($IDs as $ideaId) {
            $postItem = [
                SEOAIC_POSTS::GENERATE_THUMBNAIL_FIELD => 1,
                SEOAIC_POSTS::GENERATE_THUMBNAIL_STYLE_FIELD => $generateThumbnailStyle,
                SEOAIC_POSTS::GENERATE_THUMBNAIL_PROMPT_FIELD => $prompt,
                SEOAIC_POSTS::GENERATE_THUMBNAIL_WIDTH => $width,
                SEOAIC_POSTS::GENERATE_THUMBNAIL_HEIGHT => $height
            ];
            update_post_meta(intval($ideaId), self::MASS_GENERATION_DATA, $postItem);
        }
    }

    public function prepareData($data)
    {
        $generateThumbnailForPosts = [];
        $requestDataChunks = [];
        $IDs = $this->getIDsFromRequest($data);
        foreach ($IDs as $id) {
            $postItem = get_post_meta($id, self::MASS_GENERATION_DATA, true);
            $imageStyle = !empty($postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_STYLE_FIELD])
                ? $postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_STYLE_FIELD]
                : '';
            $prompt = !empty($postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_PROMPT_FIELD])
                ? $postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_PROMPT_FIELD]
                : '';
            $isGenerateThumbnail = !empty($postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_FIELD])
                ? $postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_FIELD]
                : 0;
            if (1 == $isGenerateThumbnail) {
                $generateThumbnailForPosts[] = [
                    'post_id'       => $id,
                    'image_style'   => $imageStyle,
                    'prompt'        => $prompt
                ];
            }
        }

        if (empty($generateThumbnailForPosts)) {
            return;
        }

        $groups = [];
        $thumbnailsGroupedByStyle = ArrayHelpers::groupBy('image_style', $generateThumbnailForPosts);

        // generate for posts grouped by image style
        foreach ($thumbnailsGroupedByStyle as $thumbnailsByStyleGroup) {
            if (empty($thumbnailsByStyleGroup)) {
                continue;
            }

            $thumbnailsGroupedByStyleAndPrompt = ArrayHelpers::groupBy('prompt', $thumbnailsByStyleGroup);

            foreach ($thumbnailsGroupedByStyleAndPrompt as $thumbnailsByStyleAndPromptGroup) {
                if (empty($thumbnailsByStyleAndPromptGroup)) {
                    continue;
                }

                $groups[] = $thumbnailsByStyleAndPromptGroup;
            }
        }


        foreach ($groups as $group) {
            if (empty($group)) {
                continue;
            }

            $postIDs = array_map(function($item) {
                return $item['post_id'];
            }, $group);

            $posts = get_posts([
                'numberposts'   => -1,
                'post_type'     => seoaic_get_post_types(),
                'post_status'   => SEOAIC_POSTS::ANY_STATUS,
                'include'       => $postIDs,
                'lang'          => '',
                'meta_query'    => [
                    [
                        'key'       => 'seoaic_posted',
                        'value'     => 1,
                        'compare'   => '='
                    ]
                ],
            ]);

            $style = $group[0]['image_style'];
            if (empty($style)) {
                $style = SEOAIC_SETTINGS::getImageGenerateStyle();
            }

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


            $requestData = [
                'posts' => [],
                'company_name'  => SEOAIC_SETTINGS::getBusinessName(),
                'company_desc'  => SEOAIC_SETTINGS::getBusinessDescription(),
                'industry'      => SEOAIC_SETTINGS::getIndustry(),
                'domain'        => $_SERVER['HTTP_HOST'],
                'image_style'   => $style,
                'prompt'        => $group[0]['prompt'],
                'thumbnail_generator' => 'flux',
            ];

            foreach ($posts as $post) {
                $postItem = get_post_meta($post->ID, self::MASS_GENERATION_DATA, true);
                $width = !empty($postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_WIDTH])
                    ? $postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_WIDTH]
                    : self::DEFAULT_THUMBNAIL_WIDTH;
                $height = !empty($postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_HEIGHT])
                    ? $postItem[SEOAIC_POSTS::GENERATE_THUMBNAIL_HEIGHT]
                    : self::DEFAULT_THUMBNAIL_HEIGHT;
                $postData = [
                    'post_id'       => $post->ID,
                    'post_title'    => $post->post_title,
                    'language'      => $this->seoaic->multilang->get_post_language($post->ID),
                    'keywords'      => '',
                    'meta_description' => '',
                    'subtitles'     => [],
                    'width'         => $width,
                    'height'        => $height
                ];

                $ideaContent = get_post_meta($post->ID, 'seoaic_idea_content', true);
                $ideaContent = !empty($ideaContent) ? json_decode($ideaContent, true) : [];

                if (!empty($ideaContent['idea_keywords'])) {
                    $postData['keywords'] = is_array($ideaContent['idea_keywords']) ? implode(',', $ideaContent['idea_keywords']) : $ideaContent['idea_keywords'];
                }
                if (!empty($ideaContent['idea_description'])) {
                    $postData['meta_description'] = $ideaContent['idea_description'];
                }
                if (!empty($ideaContent['idea_skeleton'])) {
                    $postData['subtitles'] = SEOAIC_FRAMES::formatWithOutlines($ideaContent['idea_skeleton']);
                }

                $requestData['posts'][] = $postData;
            }

            $requestDataChunks[] = $requestData;
        }

        return $requestDataChunks;
    }

    /**
     * Override parent's method to be able to make requests in a loop
     */
    public function sendActionRequest($dataChunks = [])
    {
        foreach ($dataChunks as &$dataChunk) {
            $this->debugLog(json_encode(array_map(function ($item) {
                if (
                    !empty($item['posts'])
                    && is_array($item['posts'])
                ) {
                    // foreach ($item['posts']as &$post) {
                    //     $post['content'] = substr($post['content'], 0, 50) . '...';
                    // }
                }
                return $item;
            }, [$dataChunk])));

            $dataChunk['result'] = $this->sendRequest($this->getBackendActionURL(), $dataChunk);
        }

        return $dataChunks;
    }

    public function processActionResults($dataChunks = [])
    {
        $successResults = false;
        // $loaderIDs = [];
        $this->debugLog(json_encode($dataChunks));

        if (!empty($dataChunks)) {
            foreach ($dataChunks as $dataChunk) {
                $result = $dataChunk['result'];

                if (empty($dataChunk['posts'])) {
                    continue;
                }

                if (
                    !empty($dataChunk['result'])
                    && !empty($dataChunk['result']['status'])
                    && 'success' == $dataChunk['result']['status']
                ) {
                    $successResults = true;

                    foreach ($dataChunk['posts'] as $post) {
                        $this->updatePostData($post['post_id'], [
                            $this->getStatusField() => self::GENERATING_STATUS,
                        ]);
                    }

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

                    foreach ($dataChunk['posts'] as $post) {
                        $this->updatePostData($post['post_id'], [
                            $this->getStatusField() => '',
                        ]);
                    }
                }
            }

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

            return true;

        } else {
            return false;
        }
    }

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

        if (!empty($result)) {
            if (
                !empty($result['completed'])
                && is_array($result['completed'])
            ) {
                $completedIds = $result['completed'];
                $maxExecutionTime = get_cfg_var('max_execution_time');

                if ($maxExecutionTime <= 30) { // fix for clients with low value
                    $completedIds = [$result['completed'][0]];
                }

                $returnData['done'] = $this->processCompleted($completedIds);
            }

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

        return $returnData;
    }

    protected function processCompleted($ids = [])
    {
        $return = [];

        if (
            !empty($ids)
            && is_array($ids)
        ) {
            // doublecheck the IDs to exist
            $posts = $this->getGeneratingThumbnailPostsByIDs($ids);
            if (empty($posts)) {
                return $return;
            }

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

            $thumbnailResult = $this->sendContentRequest([
                'post_ids' => $postIDs,
            ]);


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

                    $alt = !empty($item['content']['alt']) ? $item['content']['alt'] : '';
                    $readableFilename = !empty($item['content']['filename']) ? $item['content']['filename'] : '';

                    $media = ImageHelpers::uploadFromURL(
                        $item['content']['url'],
                        [
                            'post_title'        => $item['content']['title'],
                            'readable_filename' => $readableFilename,
                        ],
                        [
                            'alt' => $alt,
                        ]
                    );
                    // $this->debugLog('$media', $media);

                    $result = set_post_thumbnail($item['postId'], $media['id']);
                    // $this->seoaic->posts->debugLog('$result', $result);

                    if (!$result) {
                        $this->debugLog('[ERROR] Error on mass post thumbnail generation: post #' . $item['postId'] . '; err: error in set_post_thumbnail');

                    } else {
                        $return[$item['postId']] = [
                            'media' => $media,
                            'alt' => $alt,
                        ];
                    }

                    $this->updatePostData($item['postId'], [
                        $this->getStatusField() => self::COMPLETED_STATUS,
                    ]);
                }
            }
        }

        return $return;
    }

    protected function processFailed($ids = [])
    {
        $return = [];

        if (
            !empty($ids)
            && is_array($ids)
        ) {
            // doublecheck the IDs to exist
            $posts = $this->getGeneratingThumbnailPostsByIDs($ids);
            if (empty($posts)) {
                return $return;
            }

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

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

                $return[$origPost->ID] = [
                    'msg' => esc_html__("Thumbnail generation failed", "seoaic"),
                ];
            }
        }

        return $return;
    }

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

        return !empty($posts);
    }

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

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

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

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


    /**
     * Gets all posts that are waiting a thumbnail to be generated
     * @param array $postIDs options array of IDs to search among
     * @return array
     */
    public function getGeneratingThumbnailPostsAll()
    {
        $args = [
            'posts_per_page'    => -1,
            'post_type'         => 'any',
            'post_status'       => 'any',
            'lang'              => '', // disable default lang setting
            'meta_query'        => [
                'relation' => 'AND',
                [
                    'key' => 'seoaic_posted',
                    'value' => '1',
                    'compare' => '=',
                ],
                [
                    'key' => $this->getStatusField(),
                    'value' => self::GENERATING_STATUS,
                    'compare' => '=',
                ],
            ],
        ];

        $posts = get_posts($args);

        // $this->posts = $posts;

        return $posts;
    }

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

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

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

        $posts = get_posts($args);

        // $this->posts = $posts;

        return $posts;
    }
}
