<?php

namespace Tests\Feature\Api\V1\FullSurface;

use Illuminate\Routing\Route as LaravelRoute;
use Tests\Feature\Api\V1\FullSurface\Support\FullSurfaceE2EContractCase;

class ApiDeepIdempotencyAndDuplicateSubmissionContractTest extends FullSurfaceE2EContractCase
{
    public function test_duplicate_sensitive_mutations_do_not_create_uncontrolled_server_errors(): void
    {
        $failures = [];

        foreach ($this->duplicateSensitiveRoutes() as $route) {
            $method = $this->primaryMethod($route);
            $uri = $route->uri();
            $payload = $this->payloadFor($method, $uri) + [
                'external_reference' => 'E2E-DUP-' . md5($uri),
                'idempotency_key' => 'E2E-DUP-' . md5($uri),
            ];

            $first = $this->requestAs($this->actorFor($uri), $method, $uri, $payload);
            $second = $this->requestAs($this->actorFor($uri), $method, $uri, $payload);

            foreach (['first' => $first, 'second' => $second] as $label => $response) {
                $status = $response->getStatusCode();

                if ($status >= 500) {
                    $failures[] = "$method $uri $label duplicate request crashed with $status: " . $response->getContent();
                }

                if (! in_array($status, [200, 201, 202, 204, 302, 400, 401, 403, 404, 405, 409, 419, 422], true)) {
                    $failures[] = "$method $uri $label duplicate request returned unexpected $status: " . $response->getContent();
                }
            }
        }

        $this->assertSame([], $failures, "Duplicate submissions need controlled behavior, because users double-click like it's a sport:\n" . implode("\n", $failures));
    }

    public function test_repeat_delete_or_archive_requests_are_safe_to_retry_or_fail_cleanly(): void
    {
        $failures = [];

        foreach ($this->retryableDestructiveRoutes() as $route) {
            $uri = $route->uri();
            $first = $this->requestAs($this->actorFor($uri), 'DELETE', $uri);
            $second = $this->requestAs($this->actorFor($uri), 'DELETE', $uri);

            foreach (['first' => $first, 'second' => $second] as $label => $response) {
                if ($response->getStatusCode() >= 500) {
                    $failures[] = "DELETE $uri $label retry crashed: " . $response->getContent();
                }
            }
        }

        $this->assertSame([], $failures, "Retrying destructive operations must not crash:\n" . implode("\n", $failures));
    }

    /** @return list<LaravelRoute> */
    private function duplicateSensitiveRoutes(): array
    {
        return array_slice(array_values(array_filter($this->apiRoutes(), function (LaravelRoute $route): bool {
            if ($this->primaryMethod($route) !== 'POST') {
                return false;
            }

            $uri = $route->uri();

            return str_contains($uri, 'payments')
                || str_contains($uri, 'refund')
                || str_contains($uri, 'invoices')
                || str_contains($uri, 'attendance')
                || str_contains($uri, 'messages')
                || str_contains($uri, 'support')
                || str_contains($uri, 'students')
                || str_contains($uri, 'users')
                || str_contains($uri, 'inventory')
                || str_contains($uri, 'reimbursements');
        })), 0, 80);
    }

    /** @return list<LaravelRoute> */
    private function retryableDestructiveRoutes(): array
    {
        return array_slice(array_values(array_filter($this->apiRoutes(), function (LaravelRoute $route): bool {
            $uri = $route->uri();

            return $this->primaryMethod($route) === 'DELETE'
                || str_contains($uri, 'archive')
                || str_contains($uri, 'remove')
                || str_contains($uri, 'delete');
        })), 0, 50);
    }
}
