<?php
declare(strict_types=1);

/**
 * Template Party CMS - Security Utilities (STEP9)
 * - CSRF: tpcms_csrf_token(), tpcms_verify_csrf()
 * - Session: tpcms_start_session_secure(), tpcms_session_regenerate_on_login()
 * - Headers: tpcms_send_security_headers()  // nosniff / frame deny
 * - Escape: tpcms_h()  // 再定義防止付き
 *
 * 参照: STEP9 セキュリティ標準化（軽量）
 */

if (!function_exists('tpcms_is_https')) {
    function tpcms_is_https(): bool {
        if (!empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off') return true;
        if (!empty($_SERVER['SERVER_PORT']) && (string)$_SERVER['SERVER_PORT'] === '443') return true;
        if (!empty($_SERVER['HTTP_X_FORWARDED_PROTO']) && strtolower((string)$_SERVER['HTTP_X_FORWARDED_PROTO']) === 'https') return true;
        return false;
    }
}

if (!function_exists('tpcms_start_session_secure')) {
    function tpcms_start_session_secure(): void {
        if (session_status() === PHP_SESSION_ACTIVE) return;

        // Cookie属性：HttpOnly / Secure(https時) / SameSite=Lax
        $params = [
            'lifetime' => 0,
            'path'     => '/',
            'domain'   => '',
            'secure'   => tpcms_is_https(),
            'httponly' => true,
            'samesite' => 'Lax',
        ];
        if (PHP_VERSION_ID >= 70300) {
            session_set_cookie_params($params);
        } else {
            // 旧PHP互換（SameSite未対応環境は安全にスキップ）
            session_set_cookie_params($params['lifetime'], $params['path'], $params['domain'], $params['secure'], $params['httponly']);
        }
        @session_start();
    }
}

if (!function_exists('tpcms_session_regenerate_on_login')) {
    function tpcms_session_regenerate_on_login(): void {
        tpcms_start_session_secure();
        // ログイン直後に必ずIDを再生成（固定化攻撃対策）
        @session_regenerate_id(true);
    }
}

if (!function_exists('tpcms_send_security_headers')) {
    function tpcms_send_security_headers(): void {
        // nosniff / frame-embed deny（STEP9の最小要件）
        header('X-Content-Type-Options: nosniff');
        header('X-Frame-Options: DENY');
        // 追加で差し支えなければ参照ポリシーも絞る（任意）
        header('Referrer-Policy: same-origin');
    }
}

if (!function_exists('tpcms_send_nocache_headers')) {
    function tpcms_send_nocache_headers(): void {
        header('Cache-Control: no-store, no-cache, must-revalidate');
        header('Pragma: no-cache');
        header('Expires: 0');
    }
}

if (!function_exists('tpcms_csrf_token')) {
    function tpcms_csrf_token(): string {
        tpcms_start_session_secure();
        if (empty($_SESSION['csrf_token'])) {
            $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
        }
        return (string)$_SESSION['csrf_token'];
    }
}

if (!function_exists('tpcms_verify_csrf')) {
    function tpcms_verify_csrf(?string $token): bool {
        tpcms_start_session_secure();
        $sess = isset($_SESSION['csrf_token']) ? (string)$_SESSION['csrf_token'] : '';
        return is_string($token) && $token !== '' && $sess !== '' && hash_equals($sess, $token);
    }
}

if (!function_exists('tpcms_require_post_csrf')) {
    /**
     * POST時のみCSRF検証を行い、失敗は400で短文応答して強制終了。
     * （画面向け詳細は出さず、詳細ログは別途のハンドラで扱う想定）
     */
    function tpcms_require_post_csrf(): void {
        if (($_SERVER['REQUEST_METHOD'] ?? '') === 'POST') {
            if (!tpcms_verify_csrf($_POST['_csrf'] ?? null)) {
                http_response_code(400);
                echo 'Bad Request: invalid CSRF token';
                exit;
            }
        }
    }
}

if (!function_exists('tpcms_h')) {
    function tpcms_h($v): string {
        return htmlspecialchars((string)$v, ENT_QUOTES, 'UTF-8');
    }
}
