<?php

app_require('classes/bird.php');
app_require('modules/bird/bird_c.php');
app_require('modules/bird/config/bird_enabled_c.php');
app_require('modules/bird/config/bird_api_key_c.php');
app_require('modules/bird/config/bird_server_url_c.php');

use bird\bird_c;
use bird\config\bird_api_key_c;
use bird\config\bird_enabled_c;
use bird\config\bird_server_url_c;
use classes\bird;

if (!class_exists('BirdApiTransportCapture')) {
    class BirdApiTransportCapture extends bird
    {
        /** @var array<int,array{method:string,endpoint:string,payload:array}> */
        public array $calls = [];

        public function __construct()
        {
        }

        public function sendPostRequest(string $endpoint, array $data): array|object|null
        {
            $this->calls[] = ['method' => 'POST', 'endpoint' => $endpoint, 'payload' => $data];
            return ['method' => 'POST', 'endpoint' => $endpoint, 'payload' => $data];
        }

        public function sendGetRequest(string $endpoint, array $query = []): array|object|null
        {
            $this->calls[] = ['method' => 'GET', 'endpoint' => $endpoint, 'payload' => $query];
            return ['method' => 'GET', 'endpoint' => $endpoint, 'payload' => $query];
        }

        public function sendPatchRequest(string $endpoint, array $data = []): array|object|null
        {
            $this->calls[] = ['method' => 'PATCH', 'endpoint' => $endpoint, 'payload' => $data];
            return ['method' => 'PATCH', 'endpoint' => $endpoint, 'payload' => $data];
        }

        public function sendDeleteRequest(string $endpoint, array $query = []): array|object|null
        {
            $this->calls[] = ['method' => 'DELETE', 'endpoint' => $endpoint, 'payload' => $query];
            return ['method' => 'DELETE', 'endpoint' => $endpoint, 'payload' => $query];
        }
    }
}

if (!class_exists('BirdEnabledConfigStub')) {
    class BirdEnabledConfigStub extends bird_enabled_c
    {
        public function __construct(private bool $enabled = true)
        {
        }

        public function isTrue(): bool
        {
            return $this->enabled;
        }

        public function getVariableValue(): mixed
        {
            return $this->enabled ? 'true' : 'false';
        }
    }
}

if (!class_exists('BirdApiKeyConfigStub')) {
    class BirdApiKeyConfigStub extends bird_api_key_c
    {
        public function __construct(private string $apiKey = 'test-key')
        {
        }

        public function getVariableValue(): mixed
        {
            return $this->apiKey;
        }
    }
}

if (!class_exists('BirdServerUrlConfigStub')) {
    class BirdServerUrlConfigStub extends bird_server_url_c
    {
        public function __construct(private string $baseUrl = 'https://api.bird.test')
        {
        }

        public function getVariableValue(): mixed
        {
            return $this->baseUrl;
        }
    }
}

if (!class_exists('BirdModuleConfigStub')) {
    class BirdModuleConfigStub extends bird_c
    {
        public function __construct(string $apiKey = 'test-key', string $baseUrl = 'https://api.bird.test')
        {
            $this->enabled = new BirdEnabledConfigStub(true);
            $this->api_key = new BirdApiKeyConfigStub($apiKey);
            $this->server_url = new BirdServerUrlConfigStub($baseUrl);
        }
    }
}

if (!class_exists('BirdHttpHarness')) {
    class BirdHttpHarness extends bird
    {
        /** @var array<int,array{method:string,url:string,headers:array,body:string}> */
        public array $httpCalls = [];
        private int $stubStatus;
        private string|false|null $stubBody;

        public function __construct(int $status = 200, string|false|null $body = '{"ok":true}', string $apiKey = 'test-key', string $baseUrl = 'https://api.bird.test')
        {
            $this->stubStatus = $status;
            $this->stubBody = $body;
            $this->config = new BirdModuleConfigStub($apiKey, $baseUrl);
        }

        protected function doHttpRequest(string $method, string $url, array $headers, string $body = ''): array
        {
            $this->httpCalls[] = [
                'method' => strtoupper($method),
                'url' => $url,
                'headers' => $headers,
                'body' => $body,
            ];

            return [
                'status_code' => $this->stubStatus,
                'body' => $this->stubBody,
            ];
        }
    }
}

it('maps voice call facade methods to documented endpoints and methods', function (): void {
    $client = new BirdApiTransportCapture();

    $client->updateVoiceCall('ws', 'ch', 'call', ['status' => 'completed']);
    $client->answerVoiceCall('ws', 'ch', 'call');
    $client->ringVoiceCall('ws', 'ch', 'call');
    $client->hangupVoiceCall('ws', 'ch', 'call', ['cause' => 'busy']);
    $client->playbackVoiceCall('ws', 'ch', 'call', ['media' => ['https://example.com/a.mp3']]);
    $client->sayMessage('ws', 'ch', 'call', ['text' => 'hello']);
    $client->gatherMessage('ws', 'ch', 'call', ['maxNumKeys' => 1]);
    $client->bridgeVoiceCall('ws', 'ch', 'call', ['to' => '+4511122233']);
    $client->recordVoiceCall('ws', 'ch', 'call', ['maxLength' => 60]);

    expect($client->calls[0])->toMatchArray([
        'method' => 'PATCH',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call',
    ]);
    expect($client->calls[1])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/answer',
    ]);
    expect($client->calls[2])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/ringing',
    ]);
    expect($client->calls[3])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/hangup',
    ]);
    expect($client->calls[4])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/playback',
    ]);
    expect($client->calls[5])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/say',
    ]);
    expect($client->calls[6])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/gather',
    ]);
    expect($client->calls[7])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/bridge',
    ]);
    expect($client->calls[8])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/record',
    ]);
});

it('maps recordings insights log and flash methods to documented resources', function (): void {
    $client = new BirdApiTransportCapture();

    $client->createVoiceCallRecordingSession('ws', 'ch', 'call', ['maxLength' => 30]);
    $client->listVoiceCallRecordings('ws', 'ch', 'call', ['limit' => 10]);
    $client->getVoiceCallRecording('ws', 'ch', 'call', 'rec');
    $client->updateVoiceCallRecording('ws', 'ch', 'call', 'rec', ['status' => 'completed']);
    $client->getVoiceCallInsights('ws', 'ch', 'call');
    $client->getVoiceCallsLog('ws', ['limit' => 25]);
    $client->listFlashCalls('ws', 'ch', ['limit' => 5]);
    $client->endFlashCall('ws', 'ch', 'flash-call', ['result' => 'verified']);
    $client->hangupFlashCall('ws', 'ch', ['from' => '+4511122233', 'to' => '+4544455566']);

    expect($client->calls[0])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/recordings',
    ]);
    expect($client->calls[1])->toMatchArray([
        'method' => 'GET',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/recordings',
    ]);
    expect($client->calls[2])->toMatchArray([
        'method' => 'GET',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/recordings/rec',
    ]);
    expect($client->calls[3])->toMatchArray([
        'method' => 'PATCH',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/recordings/rec',
    ]);
    expect($client->calls[4])->toMatchArray([
        'method' => 'GET',
        'endpoint' => '/workspaces/ws/channels/ch/calls/call/insights',
    ]);
    expect($client->calls[5])->toMatchArray([
        'method' => 'GET',
        'endpoint' => '/workspaces/ws/channels/calls',
    ]);
    expect($client->calls[6])->toMatchArray([
        'method' => 'GET',
        'endpoint' => '/workspaces/ws/channels/ch/flashcalls',
    ]);
    expect($client->calls[7])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/flashcalls/flash-call',
    ]);
    expect($client->calls[8])->toMatchArray([
        'method' => 'POST',
        'endpoint' => '/workspaces/ws/channels/ch/flashcalls/hangup',
    ]);
});

it('encodes path segments before building outbound endpoints', function (): void {
    $client = new BirdApiTransportCapture();

    $client->getVoiceCall('workspace a', 'channel/a', 'call 1');
    $client->getVoiceCallRecording('workspace a', 'channel/a', 'call 1', 'recording/1');
    $client->getFlashCall('workspace a', 'channel/a', 'flash 1');

    expect($client->calls[0]['endpoint'])->toBe('/workspaces/workspace%20a/channels/channel%2Fa/calls/call%201');
    expect($client->calls[1]['endpoint'])->toBe('/workspaces/workspace%20a/channels/channel%2Fa/calls/call%201/recordings/recording%2F1');
    expect($client->calls[2]['endpoint'])->toBe('/workspaces/workspace%20a/channels/channel%2Fa/flashcalls/flash%201');
});

it('serializes query parameters for GET transport requests', function (): void {
    $client = new BirdHttpHarness();

    $response = $client->sendGetRequest('/workspaces/ws/channels/calls', [
        'limit' => 10,
        'tag' => ['vip', 'north'],
        'id' => '123',
    ]);

    expect($response)->toBeArray();
    expect($client->httpCalls)->toHaveCount(1);
    expect($client->httpCalls[0]['method'])->toBe('GET');
    expect($client->httpCalls[0]['url'])->toContain('/workspaces/ws/channels/calls?');
    expect($client->httpCalls[0]['url'])->toContain('limit=10');
    expect($client->httpCalls[0]['url'])->toContain('tag%5B0%5D=vip');
    expect($client->httpCalls[0]['url'])->toContain('tag%5B1%5D=north');
    expect($client->httpCalls[0]['url'])->toContain('id=123');
});

it('maps 4xx and 5xx transport failures into informative exceptions', function (): void {
    $client4xx = new BirdHttpHarness(422, '{"message":"invalid payload","errors":[{"message":"to is required"}]}');
    $client5xx = new BirdHttpHarness(503, '{"error":"upstream unavailable"}');

    expect(fn() => $client4xx->sendPostRequest('/workspaces/ws/channels/ch/calls', ['from' => '+4511122233']))
        ->toThrow(Exception::class, 'Bird API request failed with status 422');
    expect(fn() => $client5xx->sendGetRequest('/workspaces/ws/channels/ch/calls'))
        ->toThrow(Exception::class, 'Bird API request failed with status 503');
});
