<?php
/**
 * Created by PhpStorm.
 * User: alex
 * Date: 1/11/19
 * Time: 10:59 AM
 */

class Brizy_Admin_Blocks_Main
{

    const CP_GLOBAL = 'brizy-global-block';
    const CP_SAVED = 'brizy-saved-block';

    private $allReferencedBlocks = [];

    /**
     * @return Brizy_Admin_Blocks_Main
     */
    public static function _init()
    {
        static $instance;
        if (!$instance) {
            $instance = new self();
        }

        return $instance;
    }

    /**
     * BrizyPro_Admin_Popups constructor.
     */
    public function __construct()
    {

        if (Brizy_Editor_User::is_user_allowed()) {
            add_action('wp_loaded', array($this, 'initializeActions'));
        }
        add_action('brizy_preview_mode', array($this, 'initializePreviewActions'));
    }

    public function isReferencedInPage(Brizy_Editor_Post $block)
    {
        return isset($this->allReferencedBlocks[$block->getUid()]);
    }

    public function addReferencedInPage(Brizy_Editor_Post $block)
    {
        return $this->allReferencedBlocks[$block->getUid()] = $block;
    }

    public function initializePreviewActions($post)
    {
        // make sure we include this only in preview and only pages edited with brizy
        $is_view_page = Brizy_Public_Main::is_view_page($post);
        if ($is_view_page) {
            $this->enqueueMatchedGlobalBlockAssets($post);
        }
    }

    public function initializeActions()
    {

        add_filter('brizy_editor_config', [$this, 'addPageGlobalBlocks'], 10, 2);
        add_filter('brizy_editor_config', [$this, 'compilePageGlobalBlocks'], 10, 2);
        Brizy_Admin_Blocks_Api::_init();
    }

    /**
     * Add global block in editor config
     *
     * @param $config
     * @param $context
     *
     * @return mixed
     * @throws Exception
     */
    public function addPageGlobalBlocks($config, $context)
    {
        if (Brizy_Editor_Editor_Editor::EDITOR_CONTEXT !== $context) {
            return $config;
        }
        $blockManager = new Brizy_Admin_Blocks_Manager(Brizy_Admin_Blocks_Main::CP_GLOBAL);
        $blocks = $blockManager->getEntities(['post_status' => 'any']);
        // include all blocks
        $resultBlocks = [];
        foreach ($blocks as $block) {
            $resultBlocks[] = Brizy_Editor_Block::get($block->getWpPostId());
        }
        $config['globalBlocks'] = $blockManager->createResponseForEntities($resultBlocks, [], $context);

        return $config;

    }

    public function compilePageGlobalBlocks($config, $context)
    {
        if (Brizy_Editor_Editor_Editor::COMPILE_CONTEXT !== $context) {
            return $config;
        }
        // get page
        $post = get_post($config['wp']['page']);
        $blockManager = new Brizy_Admin_Blocks_Manager(Brizy_Admin_Blocks_Main::CP_GLOBAL);
        // get all blocks matching  by rules
        $blocks = $this->getMatchingBrizyBlocks($post);
        $blocks = array_merge($blocks,$this->getMatchingBrizyBlocks());
        // get all global block from dependencies
        if (!isset($config['globalBlocks'])) {
            $config['globalBlocks'] = [];
        }
        $tmp = array_merge($config['globalBlocks'], $blockManager->createResponseForEntities($blocks, [], $context));
        $config['globalBlocks'] = array_values(array_reduce($tmp, function ($result, $object) {
            $result[$object['uid']] = $object;

            return $result;
        }, []));

        return $config;
    }

    /**
     * This will enqueue assets for blocks that matches the current page rule set.
     * This will not include the block that are include by uid with {{ brizy_dc_global_block }} placeholder
     * @return void
     */
    public function enqueueMatchedGlobalBlockAssets($post = null)
    {
        $wpPost = null;
        if ($post instanceof Brizy_Editor_Post) {
            $wpPost = $post->getWpPost();
        }
        $matching_brizy_blocks = $this->getMatchingBrizyBlocks($wpPost);
        $matching_brizy_blocks = array_merge($matching_brizy_blocks,$this->getMatchingBrizyBlocks());
        foreach ($matching_brizy_blocks as $block) {
            Brizy_Public_AssetEnqueueManager::_init()->enqueuePost($block);
        }
    }

    static public function registerCustomPosts()
    {

        $labels = array(
            'name' => _x('Global blocks', 'post type general name'),
        );
        register_post_type(self::CP_GLOBAL, array(
            'labels' => $labels,
            'public' => false,
            'has_archive' => false,
            'description' => __bt('brizy', 'Brizy') . ' ' . __('global block.', 'brizy'),
            'publicly_queryable' => false,
            'show_ui' => false,
            'show_in_menu' => false,
            'query_var' => false,
            'capability_type' => 'page',
            'hierarchical' => false,
            'show_in_rest' => false,
            'exclude_from_search' => true,
            'supports' => array('title', 'revisions', 'page-attributes'),
        ));
        $labels = array(
            'name' => _x('Saved blocks', 'brizy'),
        );
        register_post_type(self::CP_SAVED, array(
            'labels' => $labels,
            'public' => false,
            'has_archive' => false,
            'description' => __bt('brizy', 'Brizy') . ' ' . __('global block.'),
            'publicly_queryable' => false,
            'show_ui' => false,
            'show_in_menu' => false,
            'query_var' => false,
            'capability_type' => 'page',
            'hierarchical' => false,
            'show_in_rest' => false,
            'exclude_from_search' => true,
            'supports' => array('title', 'revisions', 'page-attributes'),
        ));
        add_filter('brizy_supported_post_types', function ($posts) {
            $posts[] = self::CP_SAVED;
            $posts[] = self::CP_GLOBAL;

            return $posts;
        });
    }

    public function getMatchingBrizyBlocks($wpPost = null)
    {
        $ruleMatches = [];
        if ($wpPost instanceof WP_Post) {
            if ($wpPost->post_type == 'editor-template') {
                $ruleMatches = $this->getTemplateRuleMatches($wpPost);
            } else {
                $template = Brizy_Admin_Templates::instance()->getTemplateForCurrentPage();
                if ($template) {
                    $ruleMatches = $this->getTemplateRuleMatches($template->getWpPost());
                } else {
                    $ruleMatches[] = [
                        'applyFor' => Brizy_Admin_Rule::POSTS,
                        'entityType' => $wpPost->post_type,
                        'entityValues' => [$wpPost->ID],
                    ];
                }
            }
        } else {
            $ruleMatches = Brizy_Admin_Rules_Manager::getCurrentPageGroupAndType();
            if (isset($ruleMatches[0]['entityValues'][0])) {
                $wpPost = get_post($ruleMatches[0]['entityValues'][0]);
            }
        }
        $matching_blocks = [];
        if (count($ruleMatches)) {
            $matching_blocks = $this->findMatchingBlocks($ruleMatches);
        }
        $referenced = [];
        if ($wpPost) {
            try {
                $referenced = static::findReferencedInPage(Brizy_Editor_Post::get($wpPost));
            } catch (\Exception $e) {

            }
        }

        return array_reduce(array_merge($matching_blocks, $this->findReferencedInGlobalBlocks($matching_blocks), $referenced), function ($result, $object) {
            if ($object) {
                $result[$object->getUid()] = $object;
            }
            return $result;
        }, []);
    }

    private function getTemplateRuleMatches(WP_Post $template)
    {
        $ruleMatches[] = [
            'applyFor' => Brizy_Admin_Rule::BRIZY_TEMPLATE,
            'entityType' => $template->post_type,
            'entityValues' => [$template->ID],
        ];

        return $ruleMatches;
    }

    /**
     * @param $applyFor
     * @param $entityType
     * @param $entityValues
     *
     * @return array
     */
    private function findMatchingBlocks($ruleMatches)
    {

        $resultBlocks = array();
        $allBlocks = get_posts(array(
            'post_type' => [self::CP_GLOBAL],
            'numberposts' => -1,
            'post_status' => 'publish',
        ));
        $ruleManager = new Brizy_Admin_Rules_Manager();
        $ruleSets = [];
        foreach ($allBlocks as $aBlock) {
            $ruleSets[$aBlock->ID] = $ruleManager->getRuleSet($aBlock->ID);
        }
        $excludeIds = [];
        foreach ($ruleMatches as $ruleMatch) {
            $include = null;
            $applyFor = $ruleMatch['applyFor'];
            $entityType = $ruleMatch['entityType'];
            $entityValues = $ruleMatch['entityValues'];
            $allBlocks = Brizy_Admin_Rules_Manager::sortEntitiesByRuleWeight($allBlocks, [
                'type' => $applyFor,
                'entityType' => $entityType,
                'entityValues' => $entityValues,
            ]);
            foreach ($allBlocks as $aBlock) {
                try {
                    $var = $ruleSets[$aBlock->ID];
                    if ($var->isMatching($applyFor, $entityType, $entityValues)) {
                        $resultBlocks[$aBlock->ID] = Brizy_Editor_Block::get($aBlock);
                    } else {
                        $excludeIds[] = $aBlock->ID;
                    }
                } catch (\Exception $e) {
                    $excludeIds[] = $aBlock->ID;
                }
            }
        }
        foreach ($resultBlocks as $i => $resultBlock) {
            if (in_array($i, $excludeIds)) {
                unset($resultBlocks[$i]);
            }
        }

        return array_reduce($resultBlocks, function ($result, $object) {
            $result[$object->getUid()] = $object;

            return $result;
        }, []);

        //return array_values( $resultBlocks );
    }

    /**
     * @param array<Brizy_Editor_Block> $matching_blocks
     *
     * @return array
     * @throws Exception
     */
    private function findReferencedInGlobalBlocks($matching_blocks)
    {
        $context = Brizy_Content_ContextFactory::createContext(Brizy_Editor_Project::get(), null);
        $placeholderProvider = new Brizy_Content_Providers_GlobalBlockProvider($context);
        $extractor = new \BrizyPlaceholders\Extractor($placeholderProvider);
        $globalPopups = [];
        foreach ($matching_blocks as $block) {
            $content1 = $block->getCompiledHtml();
            if (!$content1) continue;
            list($placeholders, $placeholderInstances, $content) = $extractor->extract($content1);
            foreach ($placeholders as $i => $placeholder) {
                $attrs = $placeholder->getAttributes();
                $blockManager = new Brizy_Admin_Blocks_Manager(Brizy_Admin_Blocks_Main::CP_GLOBAL);
                if (isset($attrs['uid'])) {
                    $block = $blockManager->getEntity($attrs['uid']);
                    // store global popups to be added in footer
                    if ($placeholderInstances[$i] instanceof Brizy_Content_Placeholders_GlobalBlock) {
                        $globalPopups[$attrs['uid']] = $block;
                    }
                }
            }
        }

        return $globalPopups;
    }

    static public function findReferencedInPage(Brizy_Editor_Post $wpPost)
    {

        /**
         * @todo: Rewrite this to use dependencies.
         */
        if (!$wpPost) {
            return [];
        }
        if (is_null($wpPost->getDependencies())) {
            $blockManager = new Brizy_Admin_Blocks_Manager(Brizy_Admin_Blocks_Main::CP_GLOBAL);
            function getDependencies($allDeps, Brizy_Editor_Post $wpPost, Brizy_Admin_Blocks_Manager $blockManager)
            {
                if (!is_array($wpPost->getDependencies()) || count($wpPost->getDependencies()) == 0) {
                    return $allDeps;
                }
                $allDeps = array_merge($allDeps, $wpPost->getDependencies());
                foreach ($wpPost->getDependencies() as $dep) {
                    $block = $blockManager->getEntity($dep);
                    if ($block) {
                        $allDeps = getDependencies($allDeps, $block, $blockManager);
                    }
                }

                return $allDeps;
            }

            $deps = getDependencies([], $wpPost, $blockManager);
            $resultBlocks = array_map(function ($uid) use ($blockManager) {
                return $blockManager->getEntity($uid);
            }, $deps);
            $blocks = array_reduce($resultBlocks, function ($result, $object) {
                $result[$object->getUid()] = $object;

                return $result;
            }, []);

            return $blocks;
        }

        $html = $wpPost->getCompiledSectionManager()->buildHtml();
        if (!empty($html)) {
            $context = Brizy_Content_ContextFactory::createContext(Brizy_Editor_Project::get(), $wpPost);
            $placeholderProvider = new Brizy_Content_Providers_GlobalBlockProvider($context);
            $extractor = new \BrizyPlaceholders\Extractor($placeholderProvider);
            $blocks = [];
            list($placeholders, $placeholderInstances, $content) = $extractor->extract($html);
            foreach ($placeholders as $i => $placeholder) {
                $attrs = $placeholder->getAttributes();
                $blockManager = new Brizy_Admin_Blocks_Manager(Brizy_Admin_Blocks_Main::CP_GLOBAL);
                if (isset($attrs['uid'])) {
                    $block = $blockManager->getEntity($attrs['uid']);
                    // store global popups to be added in footer
                    if ($placeholderInstances[$i] instanceof Brizy_Content_Placeholders_GlobalBlock) {
                        $blocks[$attrs['uid']] = $block;
                    }
                }
            }

            return $blocks;
        }

        // include all blocks
        $resultBlocks = [];
        return $resultBlocks;
    }
}