<?php

// Silence may be golden, but sometimes words paint the canvas of understanding.
namespace Linksy\SearchAndReplace\Plugli\Linksy\Common\Helpers\Post\Editors;

use Linksy\SearchAndReplace\Plugli\Standard\Helpers\Database\Database;
class DefaultEditor extends Editors
{
    const SUPPORTED_SHORTCODES = ['fusion_text', 'fusion_checklist', 'fusion_li_item', 'fusion_toggle', 'et_pb_section', 'et_pb_row', 'et_pb_column', 'et_pb_text', 'et_pb_accordion', 'et_pb_accordion_item', 'et_pb_tabs', 'et_pb_tab', 'et_pb_toggle', 'cs_content', 'cs_element_layout_div', 'cs_element_text', 'cs_content_seo', 'vc_column_text', 'vc_toggle', 'vc_tta_section', 'vc_tta_tabs', 'av_textblock', 'av_iconlist_item', 'av_tab', 'av_toggle', 'av_tab_section', 'av_tab_sub_section'];
    const SUPPORTED_BLOCKS = ['divi' => ['placeholder'], 'kadence' => ['pane', 'tab'], 'greenshift-blocks' => ['text', 'iconlist', 'accordionitem', 'tabs'], 'ub' => ['content-toggle-panel-block', 'tab-block'], 'essential-blocks' => ['accordion', 'toggle-content'], 'uagb' => ['faq-child', 'icon-list-child', 'tabs-child', 'info-box'], 'genesis-blocks' => ['gb-accordion'], 'coblocks' => ['accordion'], 'generateblocks' => ['container'], 'getwid' => ['icon-box', 'tabs-item', 'accordion-item', 'toggle-item'], 'stackable' => ['text'], 'fl-builder' => ['layout'], 'core' => ['paragraph', 'list-item']];
    const UNSUPPORTED_BLOCKS = ["uagb/call-to-action", "kadence/splitcontent", "kadence/modal", "kadence/listitem", "kadence/iconlist", "kadence/infobox", "essential-blocks/team-member", "generateblocks/headline", "greenshift-blocks/titlebox", "greenshift-blocks/infobox", "greenshift-blocks/toggler", "coblocks/pricing-table", "kadence/testimonials", "getwid/image-box", "getwid/content-slider", "getwid/testimonial", "getwid/price-box", "getwid/price-list", "getwid/media-text-slider", "getwid/banner", "getwid/content-timeline", "rank-math/faq-block", "rank-math/howto-block"];
    private static $supported_blocks;
    private $acf = null;
    public function __construct($post_id)
    {
        $this->post_id = $post_id;
        $this->content = get_post_field('post_content', $post_id);
        // check if ACF is active
        if (ACF::is_active($post_id)) {
            $this->acf = new ACF($post_id);
        }
    }
    public static function is_active($post_id): bool
    {
        return \true;
    }
    private static function get_block_pattern($shortcode = '')
    {
        $shortcode = $shortcode ? preg_quote($shortcode, '/') : '[^\s>\{\}]+';
        // matches opening block tags and captures separate groups of block name and JSON data
        return '/<!--\s*wp:(' . $shortcode . ')\s*(\{.*?\})?\s*-->/s';
    }
    private function get_shortcode_pattern($shortcode = '')
    {
        $shortcode = $shortcode ? preg_quote($shortcode, '/') : '\w+';
        return '/\[(' . $shortcode . ')((?:\s+\w+\s*=\s*(?:"[^"\\\\]*+(?:\\\\.[^"\\\\]*)*"|\'[^\'\\\\]*+(?:\\\\.[^\'\\\\]*)*\'|\S*))*)\s*]/s';
    }
    private static function get_supported_blocks()
    {
        $block_names = [];
        foreach (self::SUPPORTED_BLOCKS as $addon => $blocks) {
            foreach ($blocks as $block) {
                $block_names[] = $addon === 'core' ? $block : "{$addon}/{$block}";
            }
        }
        return $block_names;
    }
    private static function has_supported_blocks($block_content)
    {
        $found = preg_match_all(self::get_block_pattern(), $block_content, $matches, \PREG_SET_ORDER);
        if ($found) {
            foreach ($matches as $match) {
                if (in_array(trim($match[1]), self::$supported_blocks)) {
                    return \true;
                }
            }
        }
        return \false;
    }
    private function has_supported_shortcodes($content)
    {
        foreach (self::SUPPORTED_SHORTCODES as $shortcode) {
            $pattern = $this->get_shortcode_pattern($shortcode);
            // check if there is a match
            if (preg_match($pattern, $content, $matches)) {
                return \true;
            }
        }
        return \false;
    }
    private static function process_blocks($content)
    {
        $pattern = self::get_block_pattern();
        $found = preg_match_all($pattern, $content, $matches, \PREG_SET_ORDER);
        if (!self::$supported_blocks) {
            self::$supported_blocks = self::get_supported_blocks();
        }
        if ($found && !empty($content)) {
            foreach ($matches as $match) {
                $block_name = trim($match[1]);
                // block closing tag
                $closing_tag = '<!-- /wp:' . $block_name . ' -->';
                // get the start position of the block
                $start_pos = strpos($content, $match[0]);
                if ($start_pos === \false) {
                    continue;
                }
                // get the start position of the block closing tag relative to the start position
                $closing_tag_pos = strpos(substr($content, $start_pos), $closing_tag);
                if ($closing_tag_pos !== \false) {
                    $closing_tag_pos -= strlen($match[0]);
                    // calculate the length of the block from opening tag to closing tag
                    $block_length = strlen($match[0]) + $closing_tag_pos + strlen($closing_tag);
                    // get block content
                    $block_content = substr($content, $start_pos + strlen($match[0]), $closing_tag_pos);
                    // clean up block based on whether the block content can be preserved or not
                    $can_be_preserved = in_array($block_name, self::$supported_blocks) || self::has_supported_blocks($block_content);
                    if (in_array($block_name, self::UNSUPPORTED_BLOCKS) || !$can_be_preserved) {
                        $content = substr_replace($content, \PHP_EOL, $start_pos, $block_length);
                    } else {
                        $content = substr_replace($content, $block_content, $start_pos, $block_length);
                    }
                }
            }
        }
        return $content;
    }
    public function process_shortcodes($content)
    {
        $pattern = $this->get_shortcode_pattern();
        $found = preg_match_all($pattern, $content, $matches, \PREG_SET_ORDER);
        // process only if there is at least a match
        if ($found && !empty($content)) {
            foreach ($matches as $match) {
                $closing_tag = '[/' . $match[1] . ']';
                // shortcode closing tag
                // get the starting position of the shortcode
                $start = strpos($content, $match[0]);
                // get the starting position of the shortcode closing tag relative to $start
                $tag_end = strpos(substr($content, $start), $closing_tag);
                if (\false !== $tag_end) {
                    // calculate the length of the shortcode content
                    $tag_end -= strlen($match[0]);
                    // get the shortcode content
                    $sc_content = substr($content, $start + strlen($match[0]), $tag_end);
                    // calculate the length of the entire shortcode
                    $length = strlen($match[0]) + $tag_end + strlen($closing_tag);
                    if (in_array($match[1], self::SUPPORTED_SHORTCODES) || self::has_supported_shortcodes($sc_content)) {
                        // replace the entire shortcode with its content only
                        $content = trim(substr_replace($content, $sc_content, $start, $length));
                    } else {
                        $content = trim(substr_replace($content, \PHP_EOL, $start, $length));
                    }
                }
            }
        }
        return $content;
    }
    public function get_content(string $post_id): string
    {
        $post_content = $this->content;
        if ($this->has_supported_shortcodes($post_content)) {
            $post_content = $this->process_shortcodes($post_content);
        }
        if (has_blocks($post_content)) {
            // clean up gutenberg blocks in the content
            $post_content = self::process_blocks($post_content);
        }
        // get ACF content if available
        if ($this->acf) {
            $post_content .= \PHP_EOL . $this->acf->get_content($post_id);
        }
        return $post_content;
    }
    public function add_link($source, $phrase, $phrase_replacement): bool
    {
        // insert link into post content
        $content = self::insert_link_in_content($this->content, $source, $phrase, $phrase_replacement);
        if ($content !== \false) {
            $this->content = $content;
            $this->has_unsaved_changes = \true;
            return \true;
        }
        // insert link into ACF content if available
        if ($this->acf) {
            return $this->acf->add_link($source, $phrase, $phrase_replacement);
        }
        return \false;
    }
    public function update_post($suggestions = null): bool
    {
        if (!$this->has_unsaved_changes) {
            return \false;
        }
        if ($this->acf) {
            $this->acf->update_post($suggestions);
        }
        $result = Database::table('posts')->where('ID', $this->post_id)->set('post_content', $this->content)->update();
        // check for wp errors
        if ($result && !is_wp_error($result)) {
            $this->has_unsaved_changes = \false;
            return \true;
        }
        return \false;
    }
    public function remove_link(string $link, string $anchor, $save = \true): bool
    {
        $pattern = '/<a\b[^>]*href="' . preg_quote($link, '/') . '"[^>]*>' . preg_quote($anchor, '/') . '<\/a>/i';
        // replace an occurrence of the link
        $content = preg_replace($pattern, $anchor, $this->content);
        if (($this->content === $content || is_null($content)) && ($this->acf && !$this->acf->remove_link($link, $anchor, $save))) {
            return \false;
        }
        $this->content = $content;
        $this->has_unsaved_changes = \true;
        if ($save) {
            return $this->update_post();
        }
        return \true;
    }
    public function replace_phrase(string $link, string $phrase, string $replacement, $save = \true): bool
    {
        $pattern = '/<a\b[^>]*href="' . preg_quote($link, '/') . '"[^>]*>' . preg_quote($phrase, '/') . '<\/a>/i';
        // replace an occurrence of the link
        $updated_content = preg_replace_callback($pattern, function ($match) use ($link, $replacement) {
            return str_replace($link, $replacement, $match[0]);
        }, $this->content);
        if (($this->content === $updated_content || is_null($updated_content)) && ($this->acf && !$this->acf->replace_phrase($link, $phrase, $replacement, $save))) {
            return \false;
        }
        $this->content = $updated_content;
        $this->has_unsaved_changes = \true;
        if ($save) {
            return $this->update_post();
        }
        return \true;
    }
}
