<?php

declare(strict_types=1);

use App\CreatorsApi\CreatorsApiClient;
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\Response;

/**
 * Carica variabili da .env (root progetto) se presenti.
 */
function loadEnv(string $path): void
{
    if (!is_file($path)) {
        return;
    }
    $lines = file($path, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
    if ($lines === false) {
        return;
    }
    foreach ($lines as $line) {
        $trim = trim($line);
        if ($trim === '' || str_starts_with($trim, '#')) {
            continue;
        }
        if (!str_contains($trim, '=')) {
            continue;
        }
        [$key, $value] = array_map('trim', explode('=', $trim, 2));
        $value = trim($value, "\"'");
        putenv("$key=$value");
        $_ENV[$key] = $value;
        $_SERVER[$key] = $value;
    }
}

loadEnv(__DIR__ . '/../.env');

/**
 * Restituisce una istanza configurata di CreatorsApiClient leggendo le env.
 */
function makeCreatorsClient(): CreatorsApiClient
{
    $config = [
        'clientId' => getenv('CREATORS_CLIENT_ID') ?: '',
        'clientSecret' => getenv('CREATORS_CLIENT_SECRET') ?: '',
        'credentialVersion' => getenv('CREATORS_CREDENTIAL_VERSION') ?: '',
        'region' => getenv('CREATORS_REGION') ?: '',
        'marketplace' => getenv('CREATORS_MARKETPLACE') ?: '',
        'partnerTag' => getenv('CREATORS_PARTNER_TAG') ?: '',
    ];

    foreach ($config as $key => $value) {
        if ($value === '') {
            test()->markTestSkipped("Variabile ambiente mancante: $key");
        }
    }

    return new CreatorsApiClient($config);
}

/**
 * Config finta per i test offline: valori non vuoti, mai usati sulla rete.
 *
 * @return array<string, string>
 */
function fakeCreatorsConfig(array $overrides = []): array
{
    return array_merge([
        'clientId' => 'test-client-id',
        'clientSecret' => 'test-client-secret',
        'credentialVersion' => '2.2',
        'region' => 'EU',
        'marketplace' => 'www.amazon.it',
        'partnerTag' => 'test-21',
    ], $overrides);
}

/**
 * Risposta del token OAuth2 (stub), sempre in testa alla coda del mock: il
 * client la richiede alla prima chiamata via lo stesso Guzzle iniettato.
 */
function tokenStubResponse(): Response
{
    return new Response(200, ['Content-Type' => 'application/json'], (string) json_encode([
        'access_token' => 'stub-access-token',
        'token_type' => 'Bearer',
        'expires_in' => 3600,
    ]));
}

/**
 * Costruisce una risposta JSON per il MockHandler.
 *
 * @param array<mixed>|string $body
 */
function jsonStubResponse(int $status, array|string $body): Response
{
    $payload = is_array($body) ? (string) json_encode($body) : $body;

    return new Response($status, ['Content-Type' => 'application/json'], $payload);
}

/**
 * CreatorsApiClient con Guzzle mockato: nessuna rete, nessuna credenziale reale.
 *
 * La coda del mock e' [token, ...risposte API]. Lo storico delle richieste in
 * uscita viene scritto in $history (per asserire path, header e body).
 *
 * @param array<int, Response|\Throwable> $apiResponses
 * @param array<int, array<string, mixed>>|null $history
 */
function mockedCreatorsClient(array $apiResponses, ?array &$history = null, array $configOverrides = []): CreatorsApiClient
{
    $queue = array_merge([tokenStubResponse()], $apiResponses);

    $stack = HandlerStack::create(new MockHandler($queue));

    $history = [];
    $stack->push(Middleware::history($history));

    $http = new Client(['handler' => $stack]);

    return new CreatorsApiClient(fakeCreatorsConfig($configOverrides), $http);
}

/**
 * Ultima richiesta HTTP registrata dallo storico del mock.
 */
function lastMockRequest(array $history): \Psr\Http\Message\RequestInterface
{
    expect($history)->not()->toBeEmpty();

    return $history[array_key_last($history)]['request'];
}
