<?php

namespace CleantalkSP\SpbctWP;

use CleantalkSP\SpbctWP\State;
use CleantalkSP\Variables\Get;

/**
 * Class to handle 'doing it wrong' errors in WordPress.
 * - If the message contains 'security-malware-firewall', it will be logged to State data. Run debug RC to know whats wrong.
 * - If the error is related to translation and the remote call action is 'scanner__controller', error will be suppressed.
 */
class DoingItWrongHandler
{
    /**
     * @var State
     */
    private $state;

    /**
     * @param State $spbc
     */
    public function __construct($spbc)
    {
        $this->state = $spbc;
        $this->addFilter();
    }

    /**
     * Add filter to handle 'doing it wrong' errors.
     *
     * @return void
     */
    private function addFilter()
    {
        add_filter('doing_it_wrong_trigger_error', array($this, 'handleErrors'), 10, 4);
    }

    /**
     * Log 'doing it wrong' error.
     *
     * @param DoingItWrongError $new_error
     *
     * @return void
     */
    private function logError($new_error)
    {
        // collect backtrace to find the source of the error
        $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 15);
        foreach ($backtrace as $_line => $data) {
            if (
                isset($data['file'], $data['line'], $data['function']) &&
                $data['function'] === '__'
            ) {
                $new_error->source = $data['file'] . ': ' . (string)$data['line'];
                break;
            }
        }
        // handle the empty case
        if (empty($this->state->data['doing_it_wrong_errors'])) {
            $this->state->data['doing_it_wrong_errors'] = array();
        }
        // reduce the number of errors to 5
        if (count($this->state->data['doing_it_wrong_errors']) >= 5) {
            $this->state->data['doing_it_wrong_errors'] = array_slice(
                $this->state->data['doing_it_wrong_errors'],
                1,
                4
            );
        }
        // save
        $this->state->data['doing_it_wrong_errors'][] = $new_error->getArray();
        $this->state->save('data');
    }

    /**
     * Hook handler for 'doing it wrong' errors.
     *
     * @param bool $is_wp_error
     * @param string $function_name
     * @param string $message
     * @param string $version
     *
     * @return bool
     * @psalm-suppress PossiblyUnusedReturnValue
     */
    public function handleErrors($is_wp_error, $function_name, $message, $version)
    {
        //skip if the error is not an error or if the function name or message is not a string
        if ( ! $is_wp_error || !is_string($function_name) || !is_string($message)) {
            return $is_wp_error;
        }

        // skip if the message does not contain 'security-malware-firewall'
        if ( false === strpos($message, 'security-malware-firewall') ) {
            return $is_wp_error;
        }

        // create a new DoingItWrongError instance
        $error = new DoingItWrongError();
        $error->message = $message;
        $error->function_name = $function_name;
        $error->version = $version;
        $error->suppressed_by = false;
        $error->time = time();
        $error->source = 'unknown';

        // suppression for textdomain translation errors
        if ($function_name === '_load_textdomain_just_in_time') {
            $error = $this->suppressTranslationErrorsOnRemoteCalls($error);
        }

        // logging to state object
        $this->logError($error);

        // if error is suppressed, return false to prevent it from being triggered
        if ( false !== $error->suppressed_by ) {
            return false;
        }

        return $is_wp_error;
    }

    /**
     * If the error is related to translation and the remote call action is 'scanner__controller',
     * suppress the error.
     * @param DoingItWrongError $error
     *
     * @return DoingItWrongError
     */
    private function suppressTranslationErrorsOnRemoteCalls($error)
    {
        if (Get::getString('spbc_remote_call_action') === 'scanner__controller') {
            $error->suppressed_by = __METHOD__;
        }
        return $error;
    }
}
