<?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\Str;
abstract class Editors
{
    const OP_CODES = ['ADD_LINK' => 0, 'REMOVE_LINK' => 1, 'EXTRACT_CONTENT' => 2, 'REPLACE_LINK' => 3];
    protected $has_unsaved_changes = \false;
    protected $link_processed = \false;
    protected $post_id = null;
    protected $content = null;
    protected $word_cnt = null;
    protected $params = [];
    abstract public function __construct($post_id);
    abstract public static function is_active(string $post_id): bool;
    abstract public function get_content(string $post_id): string;
    abstract public function replace_phrase(string $link, string $phrase, string $replacement, bool $save): bool;
    abstract public function add_link(string $source, string $phrase, string $phrase_replacement): bool;
    abstract public function remove_link(string $link, string $anchor, bool $save): bool;
    abstract public function update_post(array $suggestions = null): bool;
    /**
     * Tells whether or not the post has been modified
     *
     * @return boolean
     */
    public function has_unsaved_changes()
    {
        return $this->has_unsaved_changes;
    }
    /**
     * Returns the editor's name that this post was edited with.
     *
     * @return string
     */
    public function get_name(): string
    {
        $editor_class = explode('\\', get_class($this));
        return $editor_class[count($editor_class) - 1];
    }
    /**
     * Count the number of words present in the content of the editor.
     * NOTE: Written to be overridden by editors that use custom word counting mechanism.
     * 
     * @return integer
     */
    public function get_word_cnt(): int
    {
        if (is_null($this->word_cnt)) {
            $this->word_cnt = str_word_count(wp_strip_all_tags($this->get_content($this->post_id)));
        }
        return $this->word_cnt;
    }
    public static function insert_link_in_content($content, $source, $phrase, $phrase_replacement)
    {
        $prelude = '';
        $position = Str::position($content, $source);
        if ($position === \false) {
            return \false;
        }
        while (!empty($content) && (Str::is_in_comment($content, $source) || in_array(Str::get_enclosing_tag($content, $source), ['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'a', 'img', 'button', 'th', 'thead']))) {
            $position = Str::position($content, $source) + strlen($source);
            $prelude .= substr($content, 0, $position);
            $content = substr($content, $position);
        }
        $position = Str::position($content, $source) + strlen($prelude);
        $phrase_position = Str::position($source, $phrase);
        return Str::addSlashToUnicodeSequences(substr_replace($prelude . $content, $phrase_replacement, $position + $phrase_position, strlen($phrase)));
    }
    public static function remove_link_in_content($content, $link, $anchor)
    {
        $pattern = '/<a\b[^>]*href="' . preg_quote($link, '/') . '"[^>]*>' . preg_quote($anchor, '/') . '<\/a>/i';
        // trim out the link around the anchor
        $updated_content = preg_replace($pattern, $anchor, $content, 1);
        if ($content === $updated_content || is_null($content)) {
            return \false;
        }
        return $updated_content;
    }
    protected function after_update_post($suggestions = [])
    {
        if (!empty($suggestions)) {
            $content = get_post_field('post_content', $this->post_id);
            foreach ($suggestions as $suggestion) {
                $update = self::insert_link_in_content($content, $suggestion['source'], $suggestion['phrase'], $suggestion['replacement']);
                if ($update) {
                    $content = $update;
                }
            }
            // persist the content to the database
            wp_update_post(['ID' => $this->post_id, 'post_content' => $content]);
            return;
        }
        // call the after apply suggestions hook if save_post was not triggered
        do_action('linksy_after_apply_suggestions', $this->post_id);
    }
    protected function add_link_to_block(&$block)
    {
        if (empty($this->params)) {
            return;
        }
        $the_content = self::insert_link_in_content($block, $this->params['source'], $this->params['phrase'], $this->params['replacement']);
        if ($the_content !== \false) {
            $block = $the_content;
            $this->link_processed = \true;
        }
    }
    protected function remove_link_from_block(&$block)
    {
        if (empty($this->params)) {
            return;
        }
        $the_content = self::remove_link_in_content($block, $this->params['link'], $this->params['anchor']);
        if ($the_content !== \false) {
            $block = $the_content;
            $this->link_processed = \true;
        }
    }
    protected function replace_link_in_block(&$block)
    {
        if (empty($this->params)) {
            return;
        }
        // destructure the params array
        list('link' => $link, 'anchor' => $anchor, 'link_replacement' => $link_replacement) = $this->params;
        $pattern = '/<a\b[^>]*href="' . preg_quote($link, '/') . '"[^>]*>' . preg_quote($anchor, '/') . '<\/a>/i';
        // replace an occurrence of the link
        $updated_block = preg_replace_callback($pattern, function ($match) use ($link, $link_replacement) {
            return str_replace($link, $link_replacement, $match[0]);
        }, $block);
        if ($updated_block && $updated_block !== $block) {
            $block = $updated_block;
            $this->link_processed = \true;
        }
    }
}
