<?php

app_require('modules/bird/helpers/bird_request_validator.php');
app_require('modules/bird/helpers/bird_request_schemas.php');
app_require('traits/bird_route_validation_t.php');

use bird\helpers\bird_request_schemas;
use bird\helpers\bird_request_validator;
use traits\bird_route_validation_t;

if (!class_exists('BirdRouteValidationHarness')) {
    class BirdRouteValidationHarness
    {
        use bird_route_validation_t {
            birdValidateSchema as public validateSchema;
        }

        public int $forwardCalls = 0;

        public function validateThenForward(array $payload, array $schema): void
        {
            $this->validateSchema($payload, $schema);
            $this->forwardCalls++;
        }
    }
}

beforeEach(function (): void {
    global $response;

    $response = new class {
        public function error(mixed $data, int $status = null): void
        {
            $encoded = is_string($data) ? $data : json_encode($data);
            throw new RuntimeException('HTTP_' . (string)$status . ':' . $encoded);
        }
    };
});

it('accepts valid payloads for create and nested call flow commands', function (): void {
    $payload = [
        'from' => '+4532330288',
        'to' => '+4542331128',
        'ringTimeout' => 20,
        'notification' => ['url' => 'https://example.com/webhook'],
        'callFlow' => [
            [
                'command' => 'say',
                'conditions' => [
                    ['variable' => 'keys', 'operator' => 'eq', 'value' => '1'],
                ],
                'options' => ['text' => 'hello'],
            ],
        ],
    ];

    $errors = bird_request_validator::validate($payload, bird_request_schemas::voiceCreateCallBody());

    expect($errors)->toBe([]);
});

it('rejects unknown fields and invalid enum values in strict schemas', function (): void {
    $unknownErrors = bird_request_validator::validate(
        ['to' => '+4542331128', 'unknownField' => 'x'],
        bird_request_schemas::voiceCreateCallBody()
    );
    $enumErrors = bird_request_validator::validate(
        ['cause' => 'completed'],
        bird_request_schemas::voiceHangupBody()
    );

    expect($unknownErrors)->not->toBe([]);
    expect(implode(' | ', $unknownErrors))->toContain('$.unknownField is not allowed');

    expect($enumErrors)->not->toBe([]);
    expect(implode(' | ', $enumErrors))->toContain('$.cause must be one of: rejected, busy');
});

it('supports CSV normalization in schema validation for log filters', function (): void {
    $validQuery = [
        'channelId' => '2cbde4fd-8899-4f2d-95ea-2ab7cc6b8c97,5a6bd8c1-32b7-4a03-8b5a-b6a4a9d7a8b2',
        'tag' => 'north,vip',
    ];
    $invalidQuery = [
        'channelId' => 'not-a-uuid',
    ];

    $validErrors = bird_request_validator::validate($validQuery, bird_request_schemas::voiceCallsLogQuery());
    $invalidErrors = bird_request_validator::validate($invalidQuery, bird_request_schemas::voiceCallsLogQuery());

    expect($validErrors)->toBe([]);
    expect($invalidErrors)->not->toBe([]);
    expect(implode(' | ', $invalidErrors))->toContain('$.channelId[0] must be a UUID');
});

it('validates flash hangup payloads for both supported request shapes', function (): void {
    $resultMode = [
        'result' => 'verified',
        'receivedCli' => '+4542331128',
    ];
    $numbersMode = [
        'from' => '+4532330288',
        'to' => '+4542331128',
        'result' => 'verified',
    ];
    $invalid = [
        'result' => 'ok',
    ];

    $resultErrors = bird_request_validator::validate($resultMode, bird_request_schemas::flashHangupBody());
    $numbersErrors = bird_request_validator::validate($numbersMode, bird_request_schemas::flashHangupBody());
    $invalidErrors = bird_request_validator::validate($invalid, bird_request_schemas::flashHangupBody());

    expect($resultErrors)->toBe([]);
    expect($numbersErrors)->toBe([]);
    expect($invalidErrors)->not->toBe([]);
    expect(implode(' | ', $invalidErrors))->toContain('does not match any of the allowed schemas');
});

it('fails before forward step when strict validation fails and forwards when valid', function (): void {
    $harness = new BirdRouteValidationHarness();

    try {
        $harness->validateThenForward(['unexpected' => true], bird_request_schemas::noBody());
        $caught = null;
    } catch (RuntimeException $e) {
        $caught = $e;
    }

    expect($caught)->toBeInstanceOf(RuntimeException::class);
    expect($caught?->getMessage())->toContain('HTTP_400');
    expect($harness->forwardCalls)->toBe(0);

    $harness->validateThenForward([], bird_request_schemas::noBody());
    expect($harness->forwardCalls)->toBe(1);
});
