<?php

declare(strict_types=1);

namespace Apexl\Tests\NotifierTwilio\UnitTests;

use apexl\Io\modules\notifier\exceptions\notifierException;
use Apexl\Io\NotifierTwilio\Drivers\TwilioSmsNotifierDriver;
use Apexl\Io\NotifierTwilio\Dtos\TwilioConfigDto;
use apexl\Io\TestInfrastructure\UnitTest;
use PHPUnit\Framework\Attributes\Test;
use Twilio\Exceptions\TwilioException;
use Twilio\Rest\Client;

final class TwilioSmsNotifierDriverTest extends UnitTest
{
    #[Test]
    public function sendDelegatesToTwilioMessagesCreate(): void
    {
        $from = '+' . $this->faker->numerify('############');
        $to = '+' . $this->faker->numerify('############');
        $body = $this->faker->sentence();

        $messages = new RecordingMessageList();
        $client = $this->makeClientWithMessages($messages);
        $config = new TwilioConfigDto('AC' . $this->faker->lexify(str_repeat('?', 32)), 'token', $from);

        new TwilioSmsNotifierDriver($config, $client)->send($to, $body);

        $this->assertCount(1, $messages->calls);
        $this->assertSame($to, $messages->calls[0]['to']);
        $this->assertSame(['from' => $from, 'body' => $body], $messages->calls[0]['options']);
    }

    #[Test]
    public function sendWrapsTwilioExceptionAsNotifierException(): void
    {
        $messages = new RecordingMessageList();
        $messages->throw = new TwilioException('upstream failure', 21211);
        $client = $this->makeClientWithMessages($messages);
        $config = new TwilioConfigDto('AC123', 'token', '+447000000000');

        $driver = new TwilioSmsNotifierDriver($config, $client);

        try {
            $driver->send('+447111111111', 'hi');
            $this->fail('Expected notifierException');
        } catch (notifierException $e) {
            $this->assertStringContainsString('upstream failure', $e->getMessage());
            $this->assertSame(21211, $e->getCode());
            $this->assertInstanceOf(TwilioException::class, $e->getPrevious());
        }
    }

    private function makeClientWithMessages(RecordingMessageList $messages): Client
    {
        return new class($messages) extends Client {
            public function __construct(private readonly RecordingMessageList $stub)
            {
                // Skip parent constructor — no real credentials needed.
            }

            public function __get(string $name): mixed
            {
                if ($name === 'messages') {
                    return $this->stub;
                }

                return parent::__get($name);
            }
        };
    }
}

final class RecordingMessageList
{
    /** @var list<array{to: string, options: array<string, mixed>}> */
    public array $calls = [];
    public ?TwilioException $throw = null;

    public function create(string $to, array $options): void
    {
        $this->calls[] = ['to' => $to, 'options' => $options];

        if ($this->throw !== null) {
            throw $this->throw;
        }
    }
}
