<?php

namespace CleantalkSP\Security;

use CleantalkSP\SpbctWP\State;
use WP_Error;

/**
 * Class LoginCollectingProtector
 *
 * Protects against login data collection by securing author links and password reset errors.
 */
class LoginCollectingProtector
{
    /**
     * @var bool Indicates if password reset protection is enabled.
     */
    private $password_reset_protection_enabled;
    /**
     * @var bool Indicates if author link protection is enabled.
     */
    private $author_link_protection_enabled;

    /**
     * Constructor.
     *
     * Initializes the protection settings and applies the necessary filters and actions.
     *
     * @param State $spbc The plugin state object containing settings.
     */
    public function __construct($spbc)
    {
        $this->author_link_protection_enabled    = (bool)$spbc->settings['misc__prevent_logins_collecting'];
        $this->password_reset_protection_enabled = (bool)$spbc->settings['misc__prevent_logins_collecting_on_password_reset'];
    }

    /**
     * Initializes the class by applying filters and actions based on the settings.
     * @return void
     */
    public function init()
    {
        if (! is_admin() && $this->author_link_protection_enabled) {
            $this->secureAuthorLink();
        }

        if ($this->password_reset_protection_enabled) {
            $this->securePasswordResetError();
        }
    }

    /**
     * Secures author links by adding a filter to redirect requests to a honeypot login.
     *
     * @return void
     */
    private function secureAuthorLink()
    {
        add_filter('redirect_canonical', array($this,'redirectToHoneypotLogin'), 1, 2);
    }

    /**
     * Redirects requests containing author IDs to a honeypot login page.
     *
     * @param string $redirect The original redirect URL.
     * @param string $request The requested URL.
     *
     * @return string The modified redirect URL.
     * @psalm-suppress PossiblyUnusedReturnValue
     */
    public function redirectToHoneypotLogin($redirect, $request)
    {
        if ( preg_match('/author=\d+/i', $request) ) {
            add_filter('author_link', array($this, 'changeAuthorNameAndRedirect'), 10, 3);
        }

        return $redirect;
    }

    /**
     * Changes the author link to a honeypot login and redirects the user.
     *
     * @param string $link The original author link.
     * @param string $_author_id The author ID.
     * @param string $_author_nicename The author nicename.
     *
     * @return void
     */
    public function changeAuthorNameAndRedirect($link, $_author_id, $_author_nicename)
    {
        $link = preg_replace('@(.*?)([\w-]+\/)$@', '$1honeypot_login_' . microtime(true), $link);
        wp_redirect($link);
        die();
    }

    /**
     * Secures password reset errors by adding filters to handle and modify error messages.
     *
     * @return void
     */
    private function securePasswordResetError()
    {
        add_filter('lost_password', array($this, 'handlePasswordError'));
        add_filter('wp_login_errors', array($this,'changeWPLoginError'));
    }

    /**
     * Changes the confirmation error message for password reset.
     *
     * @return string The modified confirmation error message.
     */
    public static function changeConfirmationErrorMessage()
    {
        $message = __(
            'If the user with the specified credentials exists, check your email for the password reset confirmation link. Then visit %slogin page%s',
            'security-malware-firewall'
        );
        return sprintf(
            $message,
            '<a href="' . wp_login_url() . '">',
            '</a>'
        );
    }

    /**
     * Handles password reset errors by redirecting to the login page with a confirmation parameter.
     *
     * @param mixed $errors The error object or data.
     *
     * @return void
     */
    public function handlePasswordError($errors)
    {
        if ($errors instanceof WP_Error && !empty($errors->errors['invalidcombo'])) {
            wp_redirect(wp_login_url() . '?checkemail=confirm');
            die();
        }
    }

    /**
     * Modifies the WordPress login error messages.
     *
     * @param mixed $errors The error object or data.
     *
     * @return mixed|WP_Error The modified error object or original data.
     * @psalm-suppress PossiblyUnusedReturnValue
     */
    public function changeWPLoginError($errors)
    {
        if ($errors instanceof WP_Error &&
            !empty($errors->errors['confirm']) &&
            !empty($errors->errors['confirm'][0])
        ) {
            $errors->errors['confirm'][0] = self::changeConfirmationErrorMessage();
        }
        return $errors;
    }
}
