<?php

declare(strict_types=1);

namespace HyperfTest\Cases\Unit\Middleware;

use App\Middleware\RequestLogMiddleware;
use App\Utils\RequestHelper;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\Psr7\ServerRequest;
use PHPUnit\Framework\TestCase;

/**
 * RequestLogMiddleware 静态辅助方法单元测试
 *
 * 覆盖 sanitizeBody 敏感字段脱敏、extractResponseCode JSON 提取、getClientIp IP 获取
 *
 * @internal
 * @coversNothing
 */
class RequestLogMiddlewareTest extends TestCase
{
    // ========== sanitizeBody ==========

    public function test_sanitize_body_replaces_password_with_asterisks(): void
    {
        $body = ['username' => 'admin', 'password' => 'secret123'];
        $result = RequestLogMiddleware::sanitizeBody($body);

        $this->assertSame('admin', $result['username']);
        $this->assertSame('***', $result['password']);
    }

    public function test_sanitize_body_handles_nested_password_fields(): void
    {
        $body = [
            'user' => [
                'name' => 'test',
                'password' => 'nested_secret',
            ],
        ];
        $result = RequestLogMiddleware::sanitizeBody($body);

        $this->assertSame('test', $result['user']['name']);
        $this->assertSame('***', $result['user']['password']);
    }

    public function test_sanitize_body_handles_all_password_variants(): void
    {
        $body = [
            'password' => 'p1',
            'old_password' => 'p2',
            'new_password' => 'p3',
            'password_confirmation' => 'p4',
        ];
        $result = RequestLogMiddleware::sanitizeBody($body);

        $this->assertSame('***', $result['password']);
        $this->assertSame('***', $result['old_password']);
        $this->assertSame('***', $result['new_password']);
        $this->assertSame('***', $result['password_confirmation']);
    }

    public function test_sanitize_body_preserves_non_sensitive_fields(): void
    {
        $body = ['username' => 'admin', 'email' => 'a@b.com', 'status' => 1];
        $result = RequestLogMiddleware::sanitizeBody($body);

        $this->assertSame($body, $result);
    }

    public function test_sanitize_body_handles_empty_array(): void
    {
        $result = RequestLogMiddleware::sanitizeBody([]);
        $this->assertSame([], $result);
    }

    // ========== extractResponseCode ==========

    public function test_extract_response_code_from_json_response(): void
    {
        $response = new Response(200, ['Content-Type' => 'application/json'], json_encode([
            'code' => 0,
            'message' => 'success',
            'data' => [],
        ]));

        $code = RequestLogMiddleware::extractResponseCode($response);
        $this->assertSame(0, $code);
    }

    public function test_extract_response_code_returns_null_for_non_json(): void
    {
        $response = new Response(200, ['Content-Type' => 'text/html'], '<html></html>');

        $code = RequestLogMiddleware::extractResponseCode($response);
        $this->assertNull($code);
    }

    public function test_extract_response_code_returns_null_when_no_code_field(): void
    {
        $response = new Response(200, ['Content-Type' => 'application/json'], json_encode([
            'message' => 'ok',
        ]));

        $code = RequestLogMiddleware::extractResponseCode($response);
        $this->assertNull($code);
    }

    // ========== getClientIp ==========

    public function test_get_client_ip_from_x_forwarded_for(): void
    {
        $request = new ServerRequest('GET', '/test', ['X-Forwarded-For' => '1.2.3.4, 5.6.7.8']);

        $ip = RequestHelper::getClientIp($request);
        $this->assertSame('1.2.3.4', $ip);
    }

    public function test_get_client_ip_from_x_real_ip(): void
    {
        $request = new ServerRequest('GET', '/test', ['X-Real-IP' => '10.0.0.1']);

        $ip = RequestHelper::getClientIp($request);
        $this->assertSame('10.0.0.1', $ip);
    }

    public function test_get_client_ip_from_server_params(): void
    {
        $request = new ServerRequest('GET', '/test', [], null, '1.1', ['remote_addr' => '192.168.1.1']);

        $ip = RequestHelper::getClientIp($request);
        $this->assertSame('192.168.1.1', $ip);
    }

    public function test_get_client_ip_returns_null_when_no_ip_available(): void
    {
        $request = new ServerRequest('GET', '/test');

        $ip = RequestHelper::getClientIp($request);
        $this->assertNull($ip);
    }

    public function test_extract_response_code_handles_code_as_string(): void
    {
        $response = new Response(200, ['Content-Type' => 'application/json'], json_encode([
            'code' => '200',
            'message' => 'success',
        ]));

        $code = RequestLogMiddleware::extractResponseCode($response);
        $this->assertSame(200, $code);
    }

    public function test_sanitize_body_handles_deeply_nested_structures(): void
    {
        $body = [
            'level1' => [
                'level2' => [
                    'level3' => [
                        'password' => 'deep_secret',
                        'token' => 'deep_token',
                        'name' => 'keep_this',
                    ],
                ],
            ],
        ];
        $result = RequestLogMiddleware::sanitizeBody($body);

        $this->assertSame('***', $result['level1']['level2']['level3']['password']);
        $this->assertSame('***', $result['level1']['level2']['level3']['token']);
        $this->assertSame('keep_this', $result['level1']['level2']['level3']['name']);
    }
}
