<?php

/**
 * Class Brizy_Editor
 */
class Brizy_Editor
{

    private static $settings_key = 'post-types';

    private static $instance;

    private static $is_allowed_for_current_user = null;
    /**
     * All plugin ajax actions and enpoints are going to be prefixed with this string.
     * This will not affect the database prefix tables or option keys and post meta keys *
     *
     * @var string
     */
    private static $prefix = null;
    /**
     * @var Brizy_Content_MainProcessor
     */
    private $mainProcessor = null;

    public static function get()
    {

        if (self::$instance) {
            return self::$instance;
        }
        self::$instance = new self();

        return self::$instance;
    }

    /**
     * Return the prefix
     *
     * @param string $string
     *
     * @return string
     */
    public static function prefix($string = null)
    {

        $string = $string ? trim($string) : '';
        if (!self::$prefix) {

            $prefix = 'brizy';
            if (apply_filters('brizy_wl_enabled', false)) {
                $prefix = method_exists('BrizyPro_Admin_WhiteLabel', 'getPrefix') ? BrizyPro_Admin_WhiteLabel::_init()->getPrefix() : get_option('brizy_prefix', 'brizy');
            }
            self::$prefix = $prefix;
        }

        return self::$prefix . $string;
    }

    /**
     * @param string $string
     *
     * @return string
     * @deprecated
     * Return the prefix
     *
     */
    public static function setPrefix($string)
    {

        if ($string == '') {
            throw new Exception('The prefix cannot be empty');
        }
        update_option('brizy_prefix', $string);

        return self::$prefix = $string;
    }


    /**
     * Brizy_Editor constructor.
     */
    private function __construct()
    {

        if (is_admin()) {
            Brizy_SystemChecks::run();
        }
        // make sure the project is created
        // do not remove this! we force the project creation here.
        $project = Brizy_Editor_Project::get();
        Brizy_Editor_Compiler::checkRecompileTag();
        Brizy_Admin_Flash::instance()->initialize(); // initialize flash
        add_action('init', array($this, 'registerCustomPostTemplates'), -4000);
        add_action('init', array($this, 'runMigrations'), -3000);
        add_action('init', array('Brizy_MaintenanceMode', 'init'), -4000);
        add_action('init', array($this, 'resetPermalinks'), -2000);
        add_action('init', array($this, 'initialize'), -2000);
        add_action('init', array($this, 'initializeThirdParty'), -2000);
        add_action('init', [$this, 'handleEditorEditMode'], 0);
        add_action('wp', [$this, 'handleEditorPreviewMode']);
    }

    public function initialize()
    {

        add_action('init', array($this, 'wordpressInit'), 1000);
        add_action('wp_loaded', array($this, 'wordpressLoaded'));
        add_action('wp_print_scripts', array($this, 'forceJqueryQueue'), 99999);
        add_action('pre_get_posts', array($this, 'filterSearchPostTypes'));
        if (current_user_can(Brizy_Admin_Capabilities::CAP_EDIT_WHOLE_PAGE) || Brizy_Editor_User::is_administrator()) {
            Brizy_Admin_Rules_Api::_init();
        }
        if (!defined('WP_POST_REVISIONS') || (defined('WP_POST_REVISIONS') && WP_POST_REVISIONS !== false)) {
            add_filter("wp_revisions_to_keep", array($this, 'revisionsToKeep'), 10, 2);
        }
    }

    public function filterSearchPostTypes($query)
    {
        if (!empty($_REQUEST['brz_post_type']) && is_array($_REQUEST['brz_post_type']) && $query->is_search() && !is_admin()) {
            $allowed = array_values(get_post_types(['public' => true]));
            $requested = array_map('sanitize_key', $_REQUEST['brz_post_type']);
            $filtered = array_intersect($requested, $allowed);

            if (!empty($filtered)) {
                $query->set('post_type', array_values($filtered));
            }
        }
    }

    public function initializeThirdParty()
    {
        Brizy_Editor_ThirdParty_Main::_init();
    }

    public function runMigrations()
    {

        try {
            if (!Brizy_Editor_Project::get()->getWpPost()) {
                return;
            }
        } catch (Exception $e) {
            Brizy_Admin_Flash::instance()->add_error('On run migrations, get project throw the message: ' . $e->getMessage());
        }
        try {
            $migrationManager = new Brizy_Admin_Migrations();
            $migrationManager->runMigrations(BRIZY_VERSION);
        } catch (Brizy_Admin_Migrations_UpgradeRequiredException $e) {
            Brizy_Admin_Flash::instance()->add_error('Please upgrade Brizy to the latest version.');
            Brizy_Logger::instance()->critical('Unknown migration found. The plugin must be downgraded to the previous version');
            //throw new Exception( 'Halt plugin execution!' );
        }
    }

    public function wordpressInit()
    {

        // watch all supported posts and create meta revisions
        $metaManager = new Brizy_Admin_Post_RevisionManager();
        $metaManager->addMonitor(new Brizy_Admin_Post_BrizyPostsMonitor());
        $metaManager->addMonitor(new Brizy_Admin_Post_ProjectPostMonitor());
        //Brizy_Editor_Asset_Cleaner::_init();
        Brizy_Admin_PanelPostContent::_init();
        Brizy_Admin_Templates::instance();
        Brizy_Admin_Popups_Main::_init();
        Brizy_Admin_FormEntries::_init();
        Brizy_Admin_Fonts_Main::_init();
        Brizy_Admin_Blocks_Main::_init();
        Brizy_Admin_Stories_Main::_init();
        Brizy_Admin_Symbols_Main::_init();
        if (Brizy_Editor_User::is_user_allowed()) {
            Brizy_Admin_Svg_Main::_init();
            Brizy_Admin_Json_Main::_init();
            Brizy_Admin_Layouts_Main::_init();
            Brizy_Admin_Membership_Membership::_init();
            // the cloud will be always initialized with the exception when the white label is enabled
            if (!(apply_filters('brizy_wl_enabled', false))) {
                Brizy_Admin_Cloud::_init();
            }
        }
        $this->initializeAssetLoaders();
        $supported_post_types = $this->supported_post_types();
        $supported_post_types[] = Brizy_Admin_Templates::CP_TEMPLATE;
        foreach ($supported_post_types as $type) {
            add_filter("theme_{$type}_templates", array($this, 'registerPageTemplates'));
        }
    }

    public function wordpressLoaded()
    {

        $pid = Brizy_Editor::get()->currentPostId();
        $post = null;
        try {
            // do not delete this line
            $user = Brizy_Editor_User::get();
            if ($pid) {
                $post = Brizy_Editor_Entity::get($pid);
            }
        } catch (Exception $e) {

        }

        $this->loadEditorApi($post, $user);

        $this->loadEditorAdminSettings();
        if (!apply_filters('brizy_wl_enabled', false)) {
            if (current_user_can('manage_options')) {
                add_action('wp_dashboard_setup', 'Brizy_Admin_DashboardWidget::_init');
            }
        }
        if ((current_user_can('manage_options') && is_admin()) || (defined('WP_CLI') && WP_CLI)) {
            new Brizy_Import_Main();

        }
        add_filter('brizy_content', array($this, 'brizy_content'), 10, 3);
    }

    public function handleEditorEditMode()
    {
        $pid = Brizy_Editor::get()->currentPostId();
        if (!$pid || !Brizy_Editor_Entity::isBrizyEnabled($pid) || !Brizy_Editor_User::is_user_allowed($pid)) {
            return;
        }
        try {
            $post = Brizy_Editor_Entity::get($pid);
        } catch (Exception $e) {
            return;
        }
        try {
            $main = Brizy_Public_Main::get($post);
            $main->editMode();
        } catch (Exception $e) {
            Brizy_Logger::instance()->exception($e);
        }
    }

    public function handleEditorPreviewMode()
    {
        $pid = Brizy_Editor::get()->currentPostId();
        if (!$pid || !Brizy_Editor_Entity::isBrizyEnabled($pid)) {
            return;

        }
        try {
            $post = Brizy_Editor_Entity::get($pid);
        } catch (Exception $e) {
            return;

        }
        try {
            $context = Brizy_Content_ContextFactory::createContext(Brizy_Editor_Project::get(), $post->getWpPost());
            $this->mainProcessor = new Brizy_Content_MainProcessor($context);
            $main = Brizy_Public_Main::get($post);
            $main->previewMode();
        } catch (Exception $e) {
            Brizy_Logger::instance()->exception($e);
        }
    }

    public function revisionsToKeep($num, $post)
    {
        try {
            $revisionCount = apply_filters('brizy_revisions_max_count', BRIZY_MAX_REVISIONS_TO_KEEP);
            // $num can be -1
            if ($revisionCount > $num && $num >= 0) {
                return $num;
            }
            if (in_array($post->post_type, array(Brizy_Editor_Project::BRIZY_PROJECT))) {
                return $revisionCount;

            }
            if (Brizy_Editor_Entity::isBrizyEnabled($post->ID)) {
                $num = $revisionCount;
            }
        } catch (Exception $e) {
            Brizy_Logger::instance()->debug($e->getMessage(), array($e));
        }

        return $num;
    }

    /**
     * Reset permalinks after plugin upgrade or enable
     */
    public function resetPermalinks()
    {

        $this->registerCustomPostTemplates();
        if (defined('BRIZY_PRO_VERSION') && class_exists('BrizyPro_Main')) {
            $mainInstance = new BrizyPro_Main();
            $mainInstance->registerCustomPosts();

        }
        if (get_option('brizy-regenerate-permalinks', false)) {
            flush_rewrite_rules();
            delete_option('brizy-regenerate-permalinks');
        }
    }

    /**
     * @param $templates
     *
     * @return array
     */
    function registerPageTemplates($templates)
    {
        return array_merge($templates, array(
            Brizy_Config::BRIZY_BLANK_TEMPLATE_FILE_NAME => __bt('brizy', 'Brizy') . __(' Template', 'brizy'),
        ));
    }

    public function registerCustomPostTemplates()
    {
        Brizy_Editor_Project::registerCustomPostType();
        Brizy_Admin_Layouts_Main::registerCustomPosts();
        Brizy_Admin_Fonts_Main::registerCustomPosts();
        Brizy_Admin_FormEntries::registerCustomPost();
        Brizy_Admin_Stories_Main::registerCustomPosts();
        Brizy_Admin_Popups_Main::registerCustomPosts();
        Brizy_Admin_Blocks_Main::registerCustomPosts();
        Brizy_Admin_Templates::registerCustomPostTemplate();
    }

    /**
     * @param $project
     * @param $post
     * @param $user
     */
    private function loadEditorApi($post, $user)
    {
        try {
            new Brizy_Editor_RestExtend();
            new Brizy_Editor_API($post);
            new Brizy_Editor_BlockScreenshotApi($post);
            Brizy_Editor_Accounts_Api::_init();
            new Brizy_Editor_Forms_Api($post);
            new Brizy_Editor_AuthModal();
            // for other apis
            do_action('brizy_register_api_methods', $user, $post);
        } catch (Exception $e) {
            Brizy_Logger::instance()->exception($e);
        }
    }

    private function loadEditorAdminSettings()
    {
        try {
            new Brizy_Admin_Capabilities(Brizy_Editor_Storage_Common::instance());
            if (is_admin()) {
                Brizy_Admin_Main::instance();
                new Brizy_Admin_GettingStarted();
                $this->initFeedback();

            }
            if (is_network_admin()) {
                Brizy_Admin_NetworkSettings::_init();
            } elseif (is_admin()) {
                Brizy_Admin_Settings::_init();
            }
        } catch (Exception $exception) {
            Brizy_Admin_Flash::instance()->add_error('Unable to empty the trash. Please try again later.');
            wp_safe_redirect($_SERVER['HTTP_REFERER']);
            exit;
        }
    }

    public function brizy_content($content, $project, $wpPost, $contentType = 'document')
    {

        $context = Brizy_Content_ContextFactory::createContext($project, $wpPost);
        if (!$this->mainProcessor) {
            $this->mainProcessor = new Brizy_Content_MainProcessor($context);
        } else {
            $this->mainProcessor->setContext($context);
        }

        return $this->mainProcessor->process($content);
    }

    public function forceJqueryQueue()
    {
        if (!wp_script_is('jquery', 'enqueued')) {
            wp_enqueue_script('jquery');
        }
    }

    private function initializeAssetLoaders()
    {
        try {
            $url_builder = new Brizy_Editor_UrlBuilder(null);
            $config = null;
            $proxy = new Brizy_Public_AssetProxy($url_builder, $config);
            $crop_proxy = new Brizy_Public_CropProxy($url_builder, $config);
            $attachment_proxy = new Brizy_Public_AttachmentProxy($url_builder, $config);
            $screenshot_roxy = new Brizy_Public_BlockScreenshotProxy(new Brizy_Editor_UrlBuilder(null), $config);
        } catch (Exception $e) {
            Brizy_Logger::instance()->exception($e);
        }
    }

    /*
     * ====================================================================================================
     * =====================================================================================================
     * =====================================================================================================
     * =====================================================================================================
     * =====================================================================================================
     */
    function currentPostId()
    {
        $pid = null;
        global $wp_query;
        if ($wp_query && $wp_query->is_posts_page) {
            $pid = absint(get_queried_object_id());
        } elseif ($wp_query && ($apid = get_queried_object_id()) && (is_single() || is_page()) && $wp_query->queried_object instanceof WP_Post) {
            $pid = absint($apid);
        } elseif (function_exists('is_shop') && is_shop()) {
            $pid = wc_get_page_id('shop');
        } elseif (isset($_REQUEST[Brizy_Editor::prefix('_post')])) {
            $pid = absint($_REQUEST[Brizy_Editor::prefix('_post')]);
        } elseif (isset($_REQUEST['post'])) {
            $pid = absint($_REQUEST['post']);
        } elseif (isset($_REQUEST['page_id'])) {
            $pid = absint($_REQUEST['page_id']);
        } elseif (isset($_REQUEST['post_ID'])) {
            $pid = absint($_REQUEST['post_ID']);
        } elseif (isset($_REQUEST['id'])) {
            $pid = absint($_REQUEST['id']);
        }

        return $pid;
    }

    static public function get_slug()
    {
        return apply_filters('brizy-slug', 'brizy');
    }

    public function get_path($rel = '/')
    {
        return BRIZY_PLUGIN_PATH . DIRECTORY_SEPARATOR . ltrim($rel, DIRECTORY_SEPARATOR);
    }

    public function get_url($rel = '')
    {
        return BRIZY_PLUGIN_URL . "/" . ltrim($rel, "/");
    }

    public function get_version()
    {
        return BRIZY_VERSION;
    }

    /**
     * @param $wp_post_id
     * @param bool $throw
     *
     * @return bool
     * @throws Brizy_Editor_Exceptions_UnsupportedPostType
     */
    public static function checkIfPostTypeIsSupported($wp_post_id, $throw = true)
    {
        $type = get_post_type($wp_post_id);
        $supported_post_types = self::get()->supported_post_types();
        $supported_post_types[] = 'revision';
        if (!in_array($type, $supported_post_types)) {

            if ($throw) {
                throw new Brizy_Editor_Exceptions_UnsupportedPostType("Brizy editor doesn't support '{$type}' post type");
            } else {
                return false;
            }
        }

        return true;
    }

    public function supported_post_types()
    {
        $types = $this->get_post_types();

        return apply_filters('brizy_supported_post_types', apply_filters('brizy:post_types', $types));
    }

    public function default_supported_post_types()
    {
        return array('page', 'post');
    }

    public function get_name()
    {
        return __bt('brizy', 'Brizy');
    }

    protected function get_post_types()
    {
        try {
            return Brizy_Editor_Storage_Common::instance()->get(self::$settings_key);
        } catch (Brizy_Editor_Exceptions_NotFound $exception) {
            Brizy_Editor_Storage_Common::instance()->set(self::$settings_key, $this->default_supported_post_types());

            return $this->default_supported_post_types();
        }
    }

    public function lockProject()
    {
        if (!function_exists('wp_set_post_lock')) {
            require_once ABSPATH . 'wp-admin/includes/post.php';
        }
        wp_set_post_lock(Brizy_Editor_Project::get()->getWpPostId());
    }

    public function removeProjectLock()
    {
        delete_post_meta(Brizy_Editor_Project::get()->getWpPostId(), '_edit_lock');
    }

    public function checkIfProjectIsLocked()
    {
        if (!function_exists('wp_check_post_lock')) {
            require_once ABSPATH . 'wp-admin/includes/post.php';
        }

        return wp_check_post_lock(Brizy_Editor_Project::get()->getWpPostId());
    }


    private function initFeedback()
    {

        $feedback = true;
        if (class_exists('BrizyPro_Admin_WhiteLabel')) {

            $whiteLabel = BrizyPro_Admin_WhiteLabel::_init();
            $callable = is_callable([$whiteLabel, 'getEnabled']);
            if (($callable && $whiteLabel->getEnabled()) || !$callable) {
                $feedback = false;

            }
        }
        if ($feedback && current_user_can('manage_options')) {
            new Brizy_Admin_Feedback();
        }
    }

    /**
     * @deprecated Use Brizy_Editor_User::is_user_allowed()
     */
    public static function is_user_allowed()
    {

        if (!is_user_logged_in()) {
            return false;
        }
        if (current_user_can('manage_options')) {
            return true;
        }
        if (is_null(self::$is_allowed_for_current_user)) {
            self::$is_allowed_for_current_user = (current_user_can(Brizy_Admin_Capabilities::CAP_EDIT_WHOLE_PAGE) || current_user_can(Brizy_Admin_Capabilities::CAP_EDIT_CONTENT_ONLY));

        }

        return self::$is_allowed_for_current_user;
    }

    /**
     * Get all image sizes.
     *
     * Retrieve available image sizes with data like `width`, `height` and `crop`.
     *
     * @return array An array of available image sizes.
     */
    static public function get_all_image_sizes()
    {
        global $_wp_additional_image_sizes;
        static $image_sizes = [];
        if ($image_sizes) {
            return $image_sizes;

        }
        foreach (['thumbnail', 'medium', 'medium_large', 'large'] as $size) {
            $image_sizes[$size] = [
                'width' => (int)get_option($size . '_size_w'),
                'height' => (int)get_option($size . '_size_h'),
                'crop' => (bool)get_option($size . '_crop'),
            ];
        }
        if ($_wp_additional_image_sizes) {
            $image_sizes = array_merge($image_sizes, $_wp_additional_image_sizes);
        }
        /** The filter image_size_names_choose is documented in wp-admin/includes/media.php */
        $image_sizes = array_filter(apply_filters('image_size_names_choose', $image_sizes), function ($size) {
            return !empty($size['width']) && !empty($size['height']);
        });
        $translations = [
            'thumbnail' => __('Thumbnail'),
            'medium' => __('Medium'),
            'large' => __('Large'),
            'full' => __('Full Size'),
        ];
        foreach ($image_sizes as $sizeName => $sizeAttrs) {
            $label = isset($translations[$sizeName]) ? $translations[$sizeName] : ucwords(str_replace('_', ' ', $sizeName));
            if (is_array($sizeAttrs)) {
                $label .= sprintf(' - %d x %d', $sizeAttrs['width'], $sizeAttrs['height']);
            }
            $image_sizes[$sizeName]['label'] = $label;

        }
        if (!array_key_exists('original', $image_sizes)) {
            $image_sizes = ['original' => ['label' => __('Original', 'brizy')]] + $image_sizes;
        }

        return $image_sizes;
    }
}
