<?php
/**
 * Prohibit direct script loading
 */
defined('ABSPATH') || die('No direct script access allowed!');

/**
 * Represents a single redirect
 */
class WpmsRedirect implements ArrayAccess
{
    /**
     * Define FORMAT_PLAIN
     */
    const FORMAT_PLAIN = 'plain';
    /**
     * Define FORMAT_REGEX
     */
    const FORMAT_REGEX = 'regex';
    /**
     * Origin
     *
     * @var string
     */
    protected $origin;
    /**
     * Target URL
     *
     * @var string
     */
    protected $target = '';
    /**
     * Type
     *
     * @var integer
     */
    protected $type;
    /**
     * Format
     *
     * @var string
     */
    protected $format;

    /**
     * WpmsRedirect constructor.
     *
     * @param string  $origin Origin URL
     * @param string  $target Target URL
     * @param integer $type   Type
     * @param string  $format Format
     */
    public function __construct($origin, $target = '', $type = WPMS_REDIRECT_PERMANENT, $format = self::FORMAT_PLAIN)
    {
        $this->origin = ($format === self::FORMAT_PLAIN) ? $this->sanitizeUrl($origin) : $origin;
        $this->target = $this->sanitizeUrl($target);
        $this->format = $format;
        $this->type   = (int) $type;
    }

    /**
     * Returns the format
     *
     * @return string
     */
    public function getFormat()
    {
        return $this->format;
    }

    #[\ReturnTypeWillChange]
    /**
     * The return value will be casted to boolean if non-boolean was returned.
     *
     * @param mixed $offset Offset
     *
     * @return boolean
     */
    public function offsetExists($offset)
    {
        return in_array($offset, array('url', 'type'));
    }

    #[\ReturnTypeWillChange]
    /**
     * Offset get
     *
     * @param mixed $offset Offset
     *
     * @return integer|null|string
     */
    public function offsetGet($offset)
    {
        switch ($offset) {
            case 'old':
                return $this->origin;
            case 'url':
                return $this->target;
            case 'type':
                return $this->type;
        }

        return null;
    }

    /**
     * Returns the target
     *
     * @return string
     */
    public function getTarget()
    {
        return $this->target;
    }

    /**
     * Returns the origin.
     *
     * @return string
     */
    public function getOrigin()
    {
        return $this->origin;
    }

    /**
     * Returns the type
     *
     * @return integer
     */
    public function getType()
    {
        return $this->type;
    }

    #[\ReturnTypeWillChange]
    /**
     * Offset set
     *
     * @param mixed $offset Param
     * @param mixed $value  Value
     *
     * @return void
     */
    public function offsetSet($offset, $value)
    {
        switch ($offset) {
            case 'url':
                $this->target = $value;
                break;
            case 'type':
                $this->type = $value;
                break;
        }
    }

    #[\ReturnTypeWillChange]
    /**
     * Offset unset
     *
     * @param mixed $offset Offset
     *
     * @return void
     */
    public function offsetUnset($offset)
    {
    }

    /**
     * Compares an URL with the origin of the redirect.
     *
     * @param string $url URL
     *
     * @return boolean
     */
    public function originIs($url)
    {
        if ($this->format === self::FORMAT_PLAIN) {
            $url = $this->sanitizeSlash($url);
        }

        return (string) $this->origin === (string) $url;
    }

    /**
     * Strip the trailing slashes for relative URLs.
     *
     * @param string $url_to_sanitize URL
     *
     * @return string
     */
    private function sanitizeSlash($url_to_sanitize)
    {
        $url = $url_to_sanitize;
        if ($this->isRelativeUrl($url_to_sanitize) && $url !== '/') {
            $url = trim($url_to_sanitize, '/');
        }

        return $url;
    }

    /**
     * Is relative Url
     *
     * @param string $url URL
     *
     * @return boolean
     */
    private function isRelativeUrl($url)
    {
        $url_scheme = parse_url($url, PHP_URL_SCHEME);

        return !$url_scheme;
    }

    /**
     * Strip the protocol from the URL.
     *
     * @param string $url URL
     *
     * @return mixed
     */
    private function stripProtocol($url)
    {
        return preg_replace('(^https?://)', '', $url);
    }

    /**
     * Remove the blog's base URL from the redirect to ensure that relative URLs are created.
     *
     * @param string $url URL
     *
     * @return mixed
     */
    private function sanitizeBlogUrl($url)
    {
        $blog_url = $this->stripProtocol(get_home_url());

        if (!strpos($url, $blog_url)) {
            return $url;
        }

        $url = $this->stripProtocol($url);

        return str_replace($blog_url, '', $url);
    }

    /**
     * Sanitize the URL to remove both the blog's base URL and potential trailing slashes.
     *
     * @param string $url URL
     *
     * @return string
     */
    private function sanitizeUrl($url)
    {
        $url = $this->sanitizeBlogUrl($url);

        return $this->sanitizeSlash($url);
    }
}
