<?php

namespace Tests\Feature;

use App\Http\Controllers\ChatController;
use Illuminate\Foundation\Testing\RefreshDatabase;
use ReflectionMethod;
use RuntimeException;
use Tests\TestCase;

/**
 * What a failed turn tells the user to do next.
 *
 * A rate limit and a busy server used to share one message: "the AI provider is
 * busy. Send it again in a moment." For a 503 that is correct. For a 429 it is
 * advice that cannot work, because the window has to reset first, and it was
 * read as an instruction to keep pressing send.
 */
class FailureMessagesTest extends TestCase
{
    use RefreshDatabase;

    private function readable(\Throwable $e): string
    {
        $method = new ReflectionMethod(ChatController::class, 'readableFailure');

        return $method->invoke(app(ChatController::class), $e);
    }

    /** A quota window must not be described as a momentary blip. */
    public function test_a_rate_limit_does_not_tell_the_user_to_retry_in_a_moment(): void
    {
        $message = $this->readable(new RuntimeException('429 Too Many Requests'));

        $this->assertStringContainsString('rate limit', $message);
        $this->assertStringNotContainsString('in a moment', $message);
        $this->assertStringContainsString('fail the same way', $message);
    }

    /** A busy server still gets the advice that is right for a busy server. */
    public function test_an_overloaded_provider_still_says_try_again_shortly(): void
    {
        $message = $this->readable(new RuntimeException('503 Service Unavailable: overloaded'));

        $this->assertStringContainsString('busy', $message);
        $this->assertStringContainsString('in a moment', $message);
    }

    /** When the provider names the wait, say it. */
    public function test_a_stated_wait_is_repeated_back_in_seconds(): void
    {
        $message = $this->readable(new RuntimeException('Rate limit reached. Please try again in 30 seconds.'));

        $this->assertStringContainsString('30 seconds', $message);
    }

    /** A long wait reads as minutes rather than as a large number of seconds. */
    public function test_a_long_wait_is_expressed_in_minutes(): void
    {
        $message = $this->readable(new RuntimeException('429: rate limit, retry after 5 minutes'));

        $this->assertStringContainsString('5 minutes', $message);
        $this->assertStringNotContainsString('300 seconds', $message);
    }

    /** Credentials are still an administrator's problem, not a retry. */
    public function test_a_credential_failure_is_unchanged(): void
    {
        $message = $this->readable(new RuntimeException('401 not authorized'));

        $this->assertStringContainsString('administrator', $message);
    }

    /** An unrecognised failure is still trimmed so a JSON body cannot arrive whole. */
    public function test_an_unknown_failure_is_trimmed(): void
    {
        $message = $this->readable(new RuntimeException(str_repeat('x', 400)));

        $this->assertLessThan(200, mb_strlen($message));
    }
}
