<?php

namespace FleekDash\Admin\Iframe;

/**
 * Iframe utility methods
 *
 * Handles URL parameter preservation on redirects
 * to maintain iframe and protected mode contexts.
 *
 * @package FleekDash\Admin\Iframe
 * @since 2.2.0
 */
class IframeHelpers
{
    /**
     * Full current request URL (scheme + host + request URI). Shared by Interceptor and iframe sanitizers.
     */
    public static function getCurrentRequestFullUrl(): string
    {
        $scheme = (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on') ? 'https' : 'http';
        $host = isset($_SERVER['HTTP_HOST']) ? (string) $_SERVER['HTTP_HOST'] : '';
        $uri = isset($_SERVER['REQUEST_URI']) ? (string) $_SERVER['REQUEST_URI'] : '';

        return $scheme . '://' . $host . $uri;
    }

    /**
     * True when this admin URL uses a non-protected iframe mode (see iframe-classes.ts).
     * Implemented by GeneratedIframeAdminUrlPolicy (regenerated from TS rules — no per-plugin PHP list).
     */
    public static function shouldOmitProtectedQueryForAdminUrl(string $url): bool
    {
        if ($url === '') {
            return false;
        }

        return GeneratedIframeAdminUrlPolicy::shouldOmitProtectedQueryParam($url);
    }

    /**
     * After detectProtectedMode() copies protected from referer into $_GET, drop it when TS policy would not use ?protected=.
     */
    public static function stripProtectedSuperglobalWhenPolicyOmitsProtectedParam(): void
    {
        $full = self::getCurrentRequestFullUrl();
        if (!self::shouldOmitProtectedQueryForAdminUrl($full)) {
            return;
        }
        unset($_GET['protected']);
    }

    /**
     * @param string $location Original redirect location (before fleekdash-frame was merged).
     */
    private static function mergeProtectedAndAdminbarOntoIframeRedirect(
        string $location,
        string $newLocation,
    ): string {
        $hasProtectedParam = strpos($location, 'protected=') !== false;
        if (
            isset($_GET['protected']) && $_GET['protected'] === 'true' && !$hasProtectedParam
            && !self::shouldOmitProtectedQueryForAdminUrl($newLocation)
        ) {
            $newLocation = add_query_arg('protected', 'true', $newLocation);
        }
        if (self::shouldOmitProtectedQueryForAdminUrl($newLocation)) {
            $newLocation = remove_query_arg('protected', $newLocation);
        }

        $hasAdminbarParam = strpos($location, 'adminbar=') !== false;
        if (!$hasAdminbarParam && isset($_GET['adminbar'])) {
            return add_query_arg('adminbar', \sanitize_text_field((string) $_GET['adminbar']), $newLocation);
        }
        if (!$hasAdminbarParam && self::requestAppearsEmbeddedInFleekdashShell()) {
            $targetWantsNativeToolbar = \preg_match(
                '#/wp-admin/(post-new\\.php|post\\.php)(\\?|$)#',
                $newLocation,
            ) === 1;
            $value = $targetWantsNativeToolbar ? 'true' : 'false';

            return add_query_arg('adminbar', $value, $newLocation);
        }

        return $newLocation;
    }

    /**
     * Query values that mean "open native wp-admin plugins flow without shell iframe context"
     * (self deactivate/delete from the FleekDash UI). Must not get fleekdash-frame appended.
     *
     * @var list<string>
     */
    private const FLEEKDASH_NATIVE_PLUGINS_EXIT_VALUES = ['disabled', 'deleted'];

    /**
     * Whether fleekdash query value signals intentional exit to native plugins (no iframe chrome).
     */
    public static function isFleekdashNativePluginsExitParam(string $value): bool
    {
        return in_array($value, self::FLEEKDASH_NATIVE_PLUGINS_EXIT_VALUES, true);
    }

    /**
     * True when this admin request is almost certainly loaded inside the FleekDash shell iframe
     * (nested browsing context), not a top-level tab (new tab, address bar, bookmark).
     *
     * Do not treat {@code Referer: ...page=fleekdash} alone as iframe — new tabs opened from
     * the shell send that referer with {@code Sec-Fetch-Dest: document}.
     */
    public static function requestRunsInNestedBrowsingContext(): bool
    {
        $secFetchDest = isset($_SERVER['HTTP_SEC_FETCH_DEST'])
            ? strtolower((string) $_SERVER['HTTP_SEC_FETCH_DEST'])
            : '';

        $isNested = false;

        if ($secFetchDest === 'iframe') {
            $isNested = true;
        } elseif ($secFetchDest === 'document' || $secFetchDest === 'tab') {
            $isNested = false;
        } elseif (isset($_GET['parent_is_fleekdash'])) {
            $isNested = true;
        } elseif ($secFetchDest !== '') {
            $isNested = false;
        } else {
            $referer = isset($_SERVER['HTTP_REFERER']) ? (string) $_SERVER['HTTP_REFERER'] : '';
            $isNested = $referer !== '' && (
                strpos($referer, 'fleekdash-frame=1') !== false
                || strpos($referer, 'fleekdash-frame%3D1') !== false
            );
        }

        return $isNested;
    }

    /**
     * Broader embed signal (redirect param preservation, admin bar defaults).
     * Redirect / shell-breakout policy must use {@see requestRunsInNestedBrowsingContext} only.
     */
    public static function requestAppearsEmbeddedInFleekdashShell(): bool
    {
        if (self::requestRunsInNestedBrowsingContext()) {
            return true;
        }

        $hasFrameParam = isset($_GET['fleekdash-frame']) && (string) $_GET['fleekdash-frame'] === '1';
        if (!$hasFrameParam) {
            return false;
        }

        $secFetchDest = isset($_SERVER['HTTP_SEC_FETCH_DEST'])
            ? strtolower((string) $_SERVER['HTTP_SEC_FETCH_DEST'])
            : '';

        return $secFetchDest !== 'document' && $secFetchDest !== 'tab';
    }

    /**
     * Detect if current request is in protected mode.
     *
     * Supports direct GET requests and POST submissions that carry _wp_http_referer.
     */
    public static function isProtectedRequest(): bool
    {
        if (isset($_GET['protected']) && $_GET['protected'] === 'true') {
            return true;
        }

        $isPostRequest = isset($_SERVER['REQUEST_METHOD']) && $_SERVER['REQUEST_METHOD'] === 'POST';
        if ($isPostRequest && isset($_POST['_wp_http_referer'])) {
            $refererParts = parse_url(wp_unslash((string) $_POST['_wp_http_referer']));
            if (isset($refererParts['query'])) {
                parse_str($refererParts['query'], $params);
                if (isset($params['protected']) && $params['protected'] === 'true') {
                    return true;
                }
            }
        }

        return false;
    }

    /** Embed transport params belong on iframe src URLs, never on the FleekDash shell (page=fleekdash). */
    private const SHELL_EMBED_TRANSPORT_QUERY_KEYS = [
        'fleek-compat',
        'protected',
        'fleekdash-frame',
        'adminbar',
        'no-pop-state',
    ];

    /**
     * Preserve iframe parameter on redirects
     *
     * @param string $location The redirect location URL
     * @param int    $status   The redirect status code
     * @return string The potentially modified redirect location URL
     */
    public static function preserveIframeParameter($location, $status)
    {
        unset($status);

        $result = $location;
        $locationQuery = [];
        $locationParts = parse_url($location);
        if (isset($locationParts['query'])) {
            parse_str($locationParts['query'], $locationQuery);
        }

        $isFleekdashShell = isset($locationQuery['page']) && (string) $locationQuery['page'] === 'fleekdash';
        $isNativePluginsExit = isset($locationQuery['fleekdash'])
            && self::isFleekdashNativePluginsExitParam((string) $locationQuery['fleekdash']);

        if ($isNativePluginsExit) {
            $result = remove_query_arg(['fleekdash-frame', 'no-pop-state'], $location);
        } elseif ($isFleekdashShell) {
            $result = remove_query_arg(self::SHELL_EMBED_TRANSPORT_QUERY_KEYS, $location);
        } elseif (
            self::requestRunsInNestedBrowsingContext()
            && strpos($location, \admin_url()) === 0
            && strpos($location, 'fleekdash-frame=') === false
        ) {
            $newLocation = add_query_arg('fleekdash-frame', '1', $location);
            $newLocation = self::mergeProtectedAndAdminbarOntoIframeRedirect($location, $newLocation);
            $result = add_query_arg('no-pop-state', '1', $newLocation);
        }

        return $result;
    }

    /**
     * Preserve protected parameter on redirects
     *
     * @param string $location The redirect location URL
     * @param int    $status   The redirect status code
     * @return string The potentially modified redirect location URL
     */
    public static function preserveProtectedParameter($location, $status)
    {
        unset($status);

        $result = $location;
        $locationQuery = [];
        $locationParts = parse_url($location);
        if (isset($locationParts['query'])) {
            parse_str($locationParts['query'], $locationQuery);
        }

        $isAdminTarget = strpos($location, \admin_url()) === 0;
        $isFleekdashShell = isset($locationQuery['page']) && (string) $locationQuery['page'] === 'fleekdash';

        if ($isFleekdashShell) {
            $result = remove_query_arg('protected', $location);
        } elseif ($isAdminTarget && self::shouldOmitProtectedQueryForAdminUrl($location)) {
            $result = remove_query_arg('protected', $location);
        } elseif (
            self::isProtectedRequest()
            && $isAdminTarget
            && strpos($location, 'protected=') === false
        ) {
            $result = add_query_arg('protected', 'true', $location);
        }

        return $result;
    }
}
