<?php

namespace WPSN\Modules\Redirects;

defined('ABSPATH') || exit;

/**
 * URL helper for the Redirects module.
 */
final class RedirectsUrl
{
    public static function currentRequestUri(): string
    {
        $uri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : '';
        if ($uri === '') {
            return '';
        }
        // Keep as-is for logging (includes query string).
        return self::normalizeLeadingSlash($uri);
    }

    public static function currentPath(): string
    {
        $uri = self::currentRequestUri();
        if ($uri === '') {
            return '';
        }
        $path = wp_parse_url($uri, PHP_URL_PATH);
        if (!is_string($path)) {
            return '';
        }
        $path = rawurldecode($path);
        $path = self::normalizeLeadingSlash($path);

        // Normalize root.
        if ($path === '') {
            $path = '/';
        }

        return $path;
    }

    public static function currentAbsoluteUrl(): string
    {
        $uri = self::currentRequestUri();
        if ($uri === '') {
            return '';
        }
        return home_url($uri);
    }

    /**
     * @param array<string, mixed> $rule
     */
    public static function buildDestination(array $rule, string $requestPath, bool $preserveQuery): string
    {
        $to = isset($rule['to_url']) ? (string) $rule['to_url'] : '';
        $to = trim($to);
        if ($to === '') {
            return '';
        }

        $matchType = isset($rule['match_type']) ? (string) $rule['match_type'] : 'exact';
        $from = isset($rule['from_url']) ? (string) $rule['from_url'] : '';

        if ($matchType === 'regex') {
            $to = RedirectRegex::applyReplacement($from, $to, self::normalizeFrom($requestPath));
            if ($to === '') {
                return '';
            }
            $dest = self::toAbsoluteUrl(self::normalizeTo($to));
        } else {
            // Convert destination to absolute URL.
            $dest = self::toAbsoluteUrl($to);

            if ($matchType === 'prefix' && $from !== '') {
                $dest = self::appendRemainder($dest, $from, $requestPath);
            }
        }

        if ($preserveQuery) {
            $dest = self::appendOriginalQuery($dest);
        }

        return $dest;
    }

    public static function normalizeFrom(string $from): string
    {
        $from = trim($from);
        if ($from === '') {
            return '';
        }

        // Allow users to paste full URLs; convert to path.
        $maybePath = wp_parse_url($from, PHP_URL_PATH);
        if (is_string($maybePath) && $maybePath !== '') {
            // If input looked like a URL, use its path.
            if (preg_match('#^https?://#i', $from)) {
                $from = $maybePath;
            }
        }

        $from = rawurldecode($from);
        $from = self::normalizeLeadingSlash($from);

        // Strip query and fragment from from_url (matching uses paths).
        $from = (string) wp_parse_url($from, PHP_URL_PATH);
        if ($from === '') {
            $from = '/';
        }

        return $from;
    }

    public static function normalizeTo(string $to): string
    {
        $to = trim($to);
        if ($to === '') {
            return '';
        }

        // Allow internal relative URLs.
        if (preg_match('#^https?://#i', $to)) {
            return esc_url_raw($to);
        }

        $to = self::normalizeLeadingSlash($to);
        return esc_url_raw($to);
    }

    public static function urlsEqual(string $a, string $b): bool
    {
        // Cheap comparison that ignores trailing slashes.
        $a = rtrim($a, '/');
        $b = rtrim($b, '/');
        return $a === $b;
    }

    private static function normalizeLeadingSlash(string $path): string
    {
        $path = ltrim($path);
        if ($path === '') {
            return '';
        }
        if ($path[0] !== '/') {
            $path = '/' . $path;
        }
        return $path;
    }

    private static function toAbsoluteUrl(string $to): string
    {
        if (preg_match('#^https?://#i', $to)) {
            return $to;
        }

        // Treat as local path.
        $path = self::normalizeLeadingSlash($to);
        if ($path === '') {
            $path = '/';
        }
        return home_url($path);
    }

    private static function appendOriginalQuery(string $dest): string
    {
        $q = isset($_SERVER['QUERY_STRING']) ? (string) $_SERVER['QUERY_STRING'] : '';
        if ($q === '') {
            return $dest;
        }

        // If destination already has query string, append.
        $hasQuery = (bool) wp_parse_url($dest, PHP_URL_QUERY);
        if ($hasQuery) {
            return $dest . '&' . ltrim($q, '?');
        }

        return $dest . '?' . ltrim($q, '?');
    }

    private static function appendRemainder(string $dest, string $fromPrefix, string $requestPath): string
    {
        if ($fromPrefix === '') {
            return $dest;
        }

        // Ensure prefix and request path are normalized.
        $fromPrefix = self::normalizeFrom($fromPrefix);
        $requestPath = self::normalizeFrom($requestPath);

        if ($fromPrefix === '/' || $fromPrefix === '') {
            return $dest;
        }
        if (strpos($requestPath, $fromPrefix) !== 0) {
            return $dest;
        }

        $remainder = substr($requestPath, strlen($fromPrefix));
        $remainder = ltrim((string) $remainder, '/');
        if ($remainder === '') {
            return $dest;
        }

        $parts = wp_parse_url($dest);
        if (!is_array($parts)) {
            return $dest;
        }

        $path = isset($parts['path']) ? (string) $parts['path'] : '';
        $path = $path === '' ? '/' : $path;
        $path = rtrim($path, '/');
        $newPath = $path . '/' . $remainder;

        // Rebuild URL.
        $scheme   = isset($parts['scheme']) ? $parts['scheme'] . '://' : '';
        $user     = $parts['user'] ?? '';
        $pass     = isset($parts['pass']) ? ':' . $parts['pass']  : '';
        $auth     = $user !== '' ? $user . $pass . '@' : '';
        $host     = $parts['host'] ?? '';
        $port     = isset($parts['port']) ? ':' . $parts['port'] : '';
        $query    = isset($parts['query']) ? '?' . $parts['query'] : '';
        $fragment = isset($parts['fragment']) ? '#' . $parts['fragment'] : '';

        return $scheme . $auth . $host . $port . $newPath . $query . $fragment;
    }
}
