<?php
/**
 * Session 与 f_check 跨域（CORS）辅助
 */
class CloakSession
{
    public static function start()
    {
        if (session_status() === PHP_SESSION_ACTIVE) {
            return;
        }

        $domain = function_exists('cloak_fingerprint_cookie_domain')
            ? cloak_fingerprint_cookie_domain()
            : '';

        if ($domain !== '' && PHP_VERSION_ID >= 70300) {
            session_set_cookie_params([
                'lifetime' => 0,
                'path'     => '/',
                'domain'   => ltrim($domain, '.'),
                'secure'   => function_exists('cloak_request_is_https') && cloak_request_is_https(),
                'httponly' => true,
                'samesite' => 'Lax',
            ]);
        } elseif ($domain !== '') {
            session_set_cookie_params(
                0,
                '/',
                ltrim($domain, '.'),
                function_exists('cloak_request_is_https') && cloak_request_is_https(),
                true
            );
        }

        session_start();
    }
}

class CloakFcheckCors
{
    /**
     * OPTIONS 预检：在 session/bootstrap 之前调用。
     */
    public static function handlePreflight()
    {
        if (($_SERVER['REQUEST_METHOD'] ?? '') !== 'OPTIONS') {
            return;
        }

        self::emitHeaders();
        http_response_code(204);
        exit;
    }

    /**
     * 响应头（含 AJAX 跨子域 withCredentials）
     */
    public static function emitHeaders()
    {
        $origin = (string) ($_SERVER['HTTP_ORIGIN'] ?? '');
        if ($origin !== '' && self::isAllowedOrigin($origin)) {
            header('Access-Control-Allow-Origin: ' . $origin);
            header('Access-Control-Allow-Credentials: true');
            header('Vary: Origin');
        }

        header('Access-Control-Allow-Methods: POST, OPTIONS');
        header('Access-Control-Allow-Headers: Content-Type');
        header('Access-Control-Max-Age: 86400');
    }

    /**
     * @param string $origin
     */
    private static function isAllowedOrigin($origin)
    {
        $originHost = parse_url($origin, PHP_URL_HOST);
        if (!is_string($originHost) || $originHost === '') {
            return false;
        }

        $requestHost = function_exists('cloak_normalize_request_host')
            ? cloak_normalize_request_host($_SERVER['HTTP_HOST'] ?? '')
            : strtolower(trim((string) ($_SERVER['HTTP_HOST'] ?? '')));
        $originHost  = function_exists('cloak_normalize_request_host')
            ? cloak_normalize_request_host($originHost)
            : strtolower(trim($originHost));

        if ($requestHost !== '' && strcasecmp($originHost, $requestHost) === 0) {
            return true;
        }

        $suffix = self::registrableSuffix($requestHost);
        if ($suffix === '') {
            return false;
        }

        $originSuffix = self::registrableSuffix($originHost);
        if ($originSuffix === '' || strcasecmp($originSuffix, $suffix) !== 0) {
            return false;
        }

        return self::hostMatchesSuffix($originHost, $suffix);
    }

    /**
     * @param string $host
     */
    private static function registrableSuffix($host)
    {
        if (function_exists('cloak_registrable_domain')) {
            return cloak_registrable_domain($host);
        }

        $host = strtolower(trim($host));
        if ($host === '') {
            return '';
        }

        $parts = explode('.', $host);
        if (count($parts) < 2) {
            return $host;
        }

        return $parts[count($parts) - 2] . '.' . $parts[count($parts) - 1];
    }

    /**
     * @param string $host
     * @param string $suffix
     */
    private static function hostMatchesSuffix($host, $suffix)
    {
        $host   = strtolower($host);
        $suffix = strtolower($suffix);

        return $host === $suffix || substr($host, -strlen('.' . $suffix)) === '.' . $suffix;
    }
}
