<?php
namespace DevOwl\HeadlessContentBlocker;

use DevOwl\FastHtmlTag\finder\match\AbstractMatch;
use DevOwl\HeadlessContentBlocker\matcher\AbstractMatcher;

/**
 * A blocked result defines a "blocked" content while processing the content blocker itself.
 */
class BlockedResult {
    private $blocked = [];

    private $blockedExpressions = [];

    private $tag;

    private $attributes;

    private $markup;

    private $data = [];

    private $blockedMatchCallbacks = [];

    /**
     * Additional results which are not part of the main blocking process but should be
     * reported as well.
     *
     * @var BlockedResult[]
     */
    private $additionalResults = [];

    /**
     * C'tor.
     *
     * @param string $tag
     * @param array $attributes
     * @param Markup $markup
     */
    public function __construct($tag, $attributes, $markup) {
        $this->tag = $tag;
        $this->attributes = $attributes;
        $this->markup = $markup;
    }

    /**
     * Disable blocking.
     */
    public function disableBlocking() {
        $this->setBlocked([]);
        $this->setBlockedExpressions([]);
    }

    /**
     * Mark the content to be blocked.
     *
     * @param AbstractBlockable[] $blocked Can also be `[]` to deactivate blocking
     */
    public function setBlocked($blocked) {
        $this->blocked = $blocked;
    }

    /**
     * Add blocked description (to allow multiple blockables cover this blocked content).
     *
     * @param AbstractBlockable $blocked
     */
    public function addBlocked($blocked) {
        if (!in_array($blocked, $this->blocked, true)) {
            $this->blocked[] = $blocked;
        }
    }

    /**
     * Mark the content to be blocked through given expressions.
     *
     * @param string[] $expressions Can also be `[]`
     */
    public function setBlockedExpressions($expressions) {
        $this->blockedExpressions = $expressions;
    }

    /**
     * Mark the content to be blocked through a given expression.
     *
     * @param string $expression
     */
    public function addBlockedExpression($expression) {
        $this->blockedExpressions[] = $expression;
        $this->blockedExpressions = array_unique($this->blockedExpressions);
    }

    /**
     * Add additional result which is not part of the main blocking process but should be
     * reported as well.
     *
     * @param BlockedResult $additionalResult
     */
    public function addAdditionalResult($additionalResult) {
        $this->additionalResults[] = $additionalResult;
    }

    /**
     * Add a callable after a blocked match got found so you can alter the match again. Parameters:
     * `AbstractMatcher $matcher, AbstractMatch $match`.
     *
     * @param callable $callback
     */
    public function addBlockedMatchCallback($callback) {
        $this->blockedMatchCallbacks[] = $callback;
    }

    /**
     * Run registered blocked-match callbacks.
     *
     * @param AbstractMatcher $matcher
     * @param AbstractMatch $match
     */
    public function runBlockedMatchCallback($matcher, $match) {
        foreach ($this->blockedMatchCallbacks as $callback) {
            $callback($matcher, $match);
        }
    }

    /**
     * Allows to set additional data for this blocked result.
     *
     * @param string $key
     * @param mixed $data
     */
    public function setData($key, $data) {
        $this->data[$key] = $data;
    }

    /**
     * Get additional blocking data.
     *
     * @param string $key
     */
    public function getData($key) {
        return $this->data[$key] ?? null;
    }

    /**
     * Should this content be blocked?
     */
    public function isBlocked() {
        return count($this->blocked) > 0;
    }

    /**
     * Getter.
     */
    public function getBlocked() {
        return $this->blocked;
    }

    /**
     * Getter.
     */
    public function getFirstBlocked() {
        return count($this->blocked) > 0 ? $this->blocked[0] : null;
    }

    /**
     * Getter.
     */
    public function getBlockedExpressions() {
        return $this->blockedExpressions;
    }

    /**
     * Getter.
     */
    public function getAdditionalResults() {
        return $this->additionalResults;
    }

    /**
     * Getter.
     */
    public function getTag() {
        return $this->tag;
    }

    /**
     * Getter.
     *
     * @codeCoverageIgnore
     */
    public function getAttributes() {
        return $this->attributes;
    }

    /**
     * Getter.
     */
    public function getMarkup() {
        return $this->markup;
    }
}
