<?php

namespace Tests\Feature\Environments;

use App\Livewire\Environments\Edit;
use App\Models\Auth\User;
use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Cache;
use Livewire\Livewire;
use Spatie\Permission\Models\Permission;
use Spatie\Permission\Models\Role;
use Tests\TestCase;

/**
 * Desactivar un entorno.
 *
 * **Lo que había: no se podía.** La casilla «Activo» del formulario de edición se
 * validaba con `sometimes|accepted`, y `accepted` exige que el valor sea verdadero.
 * Desmarcarla y guardar daba «The active field must be accepted» —en inglés, junto a la
 * casilla— y el entorno **se quedaba activo**. O sea que no había ninguna forma de
 * inhabilitar un entorno desde el panel.
 *
 * `accepted` es la regla de un «acepto los términos», donde `false` es inválido. Aquí
 * `false` es la mitad del sentido del campo, así que la regla es `boolean`.
 *
 * El mismo error estaba en cinco sitios —`Create`, `CreateFromClient`, `Edit`,
 * `EnvironmentForm` y el `actived` de clientes—. En clientes tiene menos consecuencia
 * porque la baja de verdad se hace con `ClientStatusModal`, que sí funciona y avisa del
 * impacto; en entornos esa casilla era **lo único que había**.
 *
 * Lo que se fija aquí es que el estado se pueda cambiar en los dos sentidos, porque un
 * interruptor que solo va hacia un lado no es un interruptor.
 */
class DesactivarTest extends TestCase
{
    use RefreshDatabase;

    private function admin(array $permisos = ['admin.environments.index', 'admin.environments.edit']): User
    {
        $rol = Role::firstOrCreate(['name' => 'Admin', 'guard_name' => 'web']);

        foreach ($permisos as $nombre) {
            $rol->givePermissionTo(Permission::firstOrCreate(
                ['name' => $nombre, 'guard_name' => 'web'],
                ['model' => 'environments', 'description' => $nombre]
            ));
        }

        $user = User::factory()->create();
        $user->assignRole($rol);
        $this->actingAs($user);

        Cache::flush();

        return $user;
    }

    private function entorno(bool $activo = true): Environment
    {
        $cliente = Client::firstOrCreate(
            ['shortname' => 'des'],
            ['name' => 'Cliente', 'actived' => true]
        );

        return Environment::create([
            'name' => 'Sitio de pruebas',
            // Único por llamada: `domain` es la llave con la que la API encuentra el
            // entorno, y hay casos que crean dos.
            'domain' => 'https://sitio-' . (Environment::count() + 1) . '.test',
            'version' => '5.1.1',
            'env' => 'pro',
            'active' => $activo,
            'client_id' => $cliente->id,
        ]);
    }

    public function test_desmarcar_activo_desactiva_el_entorno(): void
    {
        // **Es el test que no existía.** Antes esto fallaba la validación y el entorno
        // seguía encendido.
        $this->admin();
        $entorno = $this->entorno(activo: true);

        Livewire::test(Edit::class, ['environment' => $entorno])
            ->set('active', false)
            ->call('update')
            ->assertHasNoErrors();

        $this->assertFalse((bool) $entorno->fresh()->active);
    }

    public function test_volver_a_marcarlo_lo_reactiva(): void
    {
        // Un interruptor que solo va hacia un lado no es un interruptor: si desactivar
        // funcionara y reactivar no, el arreglo estaría a medias.
        $this->admin();
        $entorno = $this->entorno(activo: false);

        Livewire::test(Edit::class, ['environment' => $entorno])
            ->set('active', true)
            ->call('update')
            ->assertHasNoErrors();

        $this->assertTrue((bool) $entorno->fresh()->active);
    }

    public function test_guardar_otro_campo_no_cambia_el_estado(): void
    {
        // El estado no debe moverse por editar el nombre: era el riesgo de cambiar la
        // regla de validación sin mirar qué hace `update()` con el valor.
        $this->admin();
        $entorno = $this->entorno(activo: false);

        Livewire::test(Edit::class, ['environment' => $entorno])
            ->set('name', 'Otro nombre')
            ->call('update')
            ->assertHasNoErrors();

        $this->assertSame('Otro nombre', $entorno->fresh()->name);
        $this->assertFalse((bool) $entorno->fresh()->active);
    }

    public function test_un_entorno_desactivado_desaparece_del_estado_del_parque(): void
    {
        // El dashboard cuenta solo los activos, pero el bloque se cachea cinco minutos
        // y nadie invalidaba la caché: desactivar un sitio y seguir viéndolo en el
        // dashboard es exactamente lo que hace que se deje de mirar un dashboard.
        $this->admin(['admin.environments.index', 'admin.environments.edit', 'admin.dashboard.index']);
        $entorno = $this->entorno(activo: true);

        // Se calienta la caché con el entorno todavía activo.
        Livewire::test(\App\Livewire\Dashboard\Index::class);

        Livewire::test(Edit::class, ['environment' => $entorno])
            ->set('active', false)
            ->call('update');

        $parque = Livewire::test(\App\Livewire\Dashboard\Index::class)->viewData('parque');

        $this->assertSame(0, $parque['total'], 'el dashboard sigue contando un entorno desactivado');
    }
}
