<?php

declare(strict_types=1);

namespace Tests\Api;

use App\Models\User;
use Database\Factories\UserFactory;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Routing\Middleware\ThrottleRequests;
use Illuminate\Support\Facades\Notification;
use Illuminate\Support\Facades\Queue;
use Illuminate\Support\Facades\Storage;
use Illuminate\Testing\TestResponse;
use Tests\TestCase;

abstract class AbstractApiTestBase extends TestCase
{
    use RefreshDatabase;

    const URI = '/V1/Api/';

    protected $seed = true;

    abstract protected static function getPath(): string;

    /**
     * @return array<int, string>
     */
    abstract public static function getStructure(): array;

    protected function setUp(): void
    {
        parent::setUp();

        $this->withoutMiddleware([ThrottleRequests::class]);
        Notification::fake();
        Queue::fake();
        Storage::fake();
    }

    /**
     * @param  array<string, mixed>  $Map
     */
    protected function createUser(array $Map = []): User
    {
        $User = UserFactory::new()->createOne($Map);

        return $User->refresh();
    }

    /**
     * @param  array<string, mixed>  $Map
     */
    protected function getJsonWithData(string $Uri, array $Map): TestResponse
    {
        return $this->json('GET', $Uri, $Map);
    }

    /**
     * @return array<int, string>
     */
    protected function getMissingKeyzz(): array
    {
        return [];
    }

    /**
     * @param  array<string, mixed>  $Parameter
     */
    public static function makeUri(string|int $Path = '', array $Parameter = []): string
    {
        $query = '';
        if (count($Parameter)) {
            $query = '?'.http_build_query($Parameter);
        }

        return static::URI.static::getPath().'/'.$Path.$query;
    }

    protected function assertMissingKeyzz(TestResponse $Response, string $path): self
    {
        foreach ($this->getMissingKeyzz() as $key) {
            $Response->assertJsonMissingPath("{$path}.{$key}");
        }

        return $this;
    }

    protected function seeData(TestResponse $Response): self
    {
        $Response->assertJsonStructure(
            [
                'Message',
                'Data',
            ]
        );

        return $this;
    }

    protected function seeItem(TestResponse $Response): self
    {
        $Response->assertJsonStructure(
            [
                'Message',
                'Item' => static::getStructure(),
            ]
        );

        return $this;
    }

    protected function seeItemzz(TestResponse $Response): self
    {
        $Response->assertJsonStructure(
            [
                'Message',
                'Itemzz' => [
                    '*' => static::getStructure(),
                ],
            ]
        );

        return $this;
    }

    protected function seePage(TestResponse $Response, bool $Simple = true, bool $Empty = false): self
    {
        $Page = [
            'from',
            'path',
            'per_page',
            'to',
            'data' => [],
        ];
        if ($Simple === false) {
            $Page[] = 'total';
        }
        if ($Empty) {
            $Response->assertJsonPath('data.data', []);
        } else {
            $Page['data'] = ['*' => static::getStructure()];
        }
        $Response->assertJsonStructure([
            'Message',
            'Page' => $Page,
        ]);

        return $this;
    }
}
