<?php

namespace Tests\Feature\Api\Admin;

use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use App\Models\Environments\Type;
use App\Models\Products\LicenseToken;
use App\Models\Products\Product;
use App\Support\ApiDeAdministracion;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Testing\TestResponse;
use Tests\TestCase;

/**
 * Lo común a los tests de la API de administración (PRODSECU-215).
 */
abstract class ApiDeAdministracionTestCase extends TestCase
{
    use RefreshDatabase;

    /**
     * Un token del usuario de servicio con estos permisos, **como lo crea la pantalla**: cada
     * `.write` arrastra su `.read` (`ApiDeAdministracion::conSusLecturas()`).
     */
    protected function token(array $permisos, string $nombre = 'test'): string
    {
        return $this->tokenExacto(ApiDeAdministracion::conSusLecturas($permisos), $nombre);
    }

    /** Un token con exactamente estos permisos, sin añadir nada. Para probar los que faltan. */
    protected function tokenExacto(array $permisos, string $nombre = 'test'): string
    {
        return ApiDeAdministracion::usuarioDeServicio()->createToken($nombre, $permisos)->plainTextToken;
    }

    /** Un token con todos los permisos de la API. */
    protected function tokenCompleto(): string
    {
        return $this->token(array_keys(ApiDeAdministracion::PERMISOS));
    }

    /**
     * Una petición a la API con ese token (o sin ninguno).
     *
     * **`forgetGuards()` antes de cada una**: el guard de Sanctum guarda el usuario que
     * resolvió en la primera petición y, en un test, todas las peticiones comparten la
     * aplicación. Sin esto, la segunda petición de un test entraría con el token de la
     * primera aunque no mandara ninguno. En producción cada petición es un proceso nuevo.
     */
    protected function api(string $metodo, string $uri, array $datos = [], ?string $token = null): TestResponse
    {
        $this->app['auth']->forgetGuards();

        $peticion = $token === null ? $this->withoutToken() : $this->withToken($token);

        return $peticion->json($metodo, '/api/admin/v1/' . ltrim($uri, '/'), $datos);
    }

    protected function cliente(string $shortname = 'uejemplo', array $extra = []): Client
    {
        return Client::create([
            'name' => 'Cliente ' . $shortname,
            'shortname' => $shortname,
            'actived' => true,
            ...$extra,
        ]);
    }

    protected function tipo(string $shortname = 'moodle'): Type
    {
        return Type::create(['name' => ucfirst($shortname), 'shortname' => $shortname]);
    }

    /** @param  list<Type>  $compatibles */
    protected function producto(string $slug, array $compatibles = [], array $extra = []): Product
    {
        $producto = Product::create([
            'slug' => $slug,
            'name' => ucfirst($slug),
            'type' => 'plugin',
            'tech' => 'moodle',
            'actived' => true,
            'enabled' => true,
            ...$extra,
        ]);

        $producto->types()->attach(array_map(fn (Type $t) => $t->id, $compatibles));

        return $producto;
    }

    protected function licencia(Client $cliente, string $token = '3IP-TEST-0000-0000-0001'): LicenseToken
    {
        return LicenseToken::create([
            'client_id' => $cliente->id,
            'name' => 'Licencia ' . $cliente->shortname,
            'token' => $token,
            'active' => true,
        ]);
    }

    protected function entorno(Client $cliente, string $dominio, array $extra = []): Environment
    {
        return Environment::create([
            'name' => 'Entorno ' . $dominio,
            'domain' => $dominio,
            'version' => '4.5',
            'env' => 'pro',
            'client_id' => $cliente->id,
            'active' => true,
            ...$extra,
        ]);
    }
}
