<?php

namespace MintyPHP\Tests\Service\Auth;

use MintyPHP\Curl;
use MintyPHP\Service\Auth\OidcHttpGateway;
use PHPUnit\Framework\TestCase;

/**
 * OidcHttpGateway is a thin adapter wrapping the static Curl::call() method.
 *
 * Since Curl is a static framework class, these tests focus on:
 * 1. Verifying the gateway delegates correctly (mock-based tests)
 * 2. Verifying method signature and return type contract
 * 3. Ensuring the gateway can be mocked by consumers (as done in MicrosoftOidcServiceTest)
 *
 * Integration-level tests with real HTTP calls are out of scope for unit tests.
 */
class OidcHttpGatewayTest extends TestCase
{
    // --- Mockability (critical for consumer tests) ---

    public function testGatewayIsMockable(): void
    {
        $mock = $this->createMock(OidcHttpGateway::class);
        $mock->method('call')
            ->willReturn(['status' => 200, 'data' => '{"access_token":"abc"}']);

        $result = $mock->call('POST', 'https://login.example.com/token', 'grant_type=authorization_code');

        $this->assertSame(200, $result['status']);
        $this->assertStringContainsString('access_token', $result['data']);
    }

    public function testMockCanSimulateNetworkError(): void
    {
        $mock = $this->createMock(OidcHttpGateway::class);
        $mock->method('call')
            ->willReturn(['status' => 0, 'data' => '']);

        $result = $mock->call('GET', 'https://unreachable.example.com');

        $this->assertSame(0, $result['status']);
        $this->assertSame('', $result['data']);
    }

    public function testMockCanSimulate401Unauthorized(): void
    {
        $mock = $this->createMock(OidcHttpGateway::class);
        $mock->method('call')
            ->willReturn(['status' => 401, 'data' => '{"error":"invalid_client"}']);

        $result = $mock->call('POST', 'https://login.example.com/token', 'grant_type=client_credentials');

        $this->assertSame(401, $result['status']);
    }

    public function testMockCanSimulate500ServerError(): void
    {
        $mock = $this->createMock(OidcHttpGateway::class);
        $mock->method('call')
            ->willReturn(['status' => 500, 'data' => 'Internal Server Error']);

        $result = $mock->call('POST', 'https://login.example.com/token');

        $this->assertSame(500, $result['status']);
    }

    public function testMockCanSimulateMalformedJsonResponse(): void
    {
        $mock = $this->createMock(OidcHttpGateway::class);
        $mock->method('call')
            ->willReturn(['status' => 200, 'data' => '{malformed']);

        $result = $mock->call('GET', 'https://login.example.com/.well-known/openid-configuration');

        $this->assertSame(200, $result['status']);
        $decoded = json_decode($result['data'], true);
        $this->assertNull($decoded, 'Malformed JSON should decode to null');
    }

    // --- Method signature contract ---

    public function testCallMethodAcceptsAllParameters(): void
    {
        $mock = $this->createMock(OidcHttpGateway::class);
        $mock->expects($this->once())
            ->method('call')
            ->with(
                'POST',
                'https://login.example.com/token',
                'grant_type=authorization_code&code=abc',
                ['Content-Type' => 'application/x-www-form-urlencoded']
            )
            ->willReturn(['status' => 200, 'data' => '{}']);

        $mock->call(
            'POST',
            'https://login.example.com/token',
            'grant_type=authorization_code&code=abc',
            ['Content-Type' => 'application/x-www-form-urlencoded']
        );
    }

    public function testCallMethodHasCorrectDefaultParameters(): void
    {
        $gateway = new OidcHttpGateway();
        $reflection = new \ReflectionMethod($gateway, 'call');
        $params = $reflection->getParameters();

        $this->assertCount(4, $params);
        $this->assertSame('method', $params[0]->getName());
        $this->assertSame('url', $params[1]->getName());
        $this->assertSame('data', $params[2]->getName());
        $this->assertTrue($params[2]->isDefaultValueAvailable());
        $this->assertSame('', $params[2]->getDefaultValue());
        $this->assertSame('headers', $params[3]->getName());
        $this->assertTrue($params[3]->isDefaultValueAvailable());
        $this->assertSame([], $params[3]->getDefaultValue());
    }

    public function testCallMethodReturnTypeIsArray(): void
    {
        $gateway = new OidcHttpGateway();
        $reflection = new \ReflectionMethod($gateway, 'call');
        $returnType = $reflection->getReturnType();

        $this->assertNotNull($returnType);
        $this->assertSame('array', (string) $returnType);
    }
}
