<?php

namespace Tests\Feature\Api;

use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use App\Models\Products\LicenseToken;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

/**
 * Por qué no se ha encontrado el entorno.
 *
 * El middleware busca el host **solo entre los entornos de la licencia que llama**, así
 * que un entorno que existe y no está vinculado a ella recibía exactamente el mismo
 * mensaje que un dominio mal escrito: «Environment not found for this host». Quien lo lee
 * se va a revisar el dominio —que está perfecto— y el problema es la vinculación.
 *
 * No es un caso raro: en la base real hay **17 entornos sin ninguna licencia**, que son
 * altas a medio configurar o sitios a la espera. Cualquiera que empiece a llamar recibe
 * este error.
 *
 * **El límite de lo que se puede decir.** Solo se explica si el entorno es del **mismo
 * cliente** que la licencia. Si es de otro, el motivo se queda en el log: confirmar a
 * quien llama que un dominio cualquiera está registrado en el Manager convertiría esta
 * respuesta en un comprobador de dominios de otros clientes. Misma norma que MGR-009 y
 * MGR-034.
 */
class HostNotFoundReasonTest extends TestCase
{
    use RefreshDatabase;

    private function cliente(string $nombre): Client
    {
        return Client::create([
            'name' => $nombre,
            'shortname' => 'c' . uniqid(),
            'actived' => true,
        ]);
    }

    private function licencia(Client $cliente): LicenseToken
    {
        return LicenseToken::create([
            'client_id' => $cliente->id,
            'name' => 'Licencia de ' . $cliente->name,
            'token' => '3IP-HOST-' . strtoupper(uniqid()),
            'active' => true,
            'start_at' => now()->subMonth(),
            'end_at' => now()->addYear(),
        ]);
    }

    private function entorno(Client $cliente, string $dominio, ?LicenseToken $licencia = null): Environment
    {
        return Environment::create([
            'client_id' => $cliente->id,
            'license_token_id' => $licencia?->id,
            'name' => 'Sitio ' . uniqid(),
            'domain' => $dominio,
            'version' => '5.1.1',
            'env' => 'pro',
            'active' => true,
        ]);
    }

    private function llamar(LicenseToken $licencia, string $host): \Illuminate\Testing\TestResponse
    {
        return $this->withHeaders([
            'Authorization' => 'Bearer ' . $licencia->token,
            'Accept' => 'application/json',
        ])->postJson('/api/v1', [
            'action' => 'products',
            'host' => $host,
            'plugin' => 'local_tresipunt',
        ]);
    }

    public function test_un_entorno_del_cliente_sin_licencia_lo_dice(): void
    {
        $cliente = $this->cliente('Cliente A');
        $licencia = $this->licencia($cliente);
        // Existe, es suyo, y no cuelga de ninguna licencia.
        $this->entorno($cliente, 'https://a-la-espera.test');

        $this->llamar($licencia, 'https://a-la-espera.test')
            ->assertUnauthorized()
            ->assertJsonPath('error', fn ($error) => str_contains($error, 'has no licence assigned yet'));
    }

    public function test_un_entorno_del_cliente_en_otra_licencia_lo_dice(): void
    {
        $cliente = $this->cliente('Cliente B');
        $licencia = $this->licencia($cliente);
        $otra = $this->licencia($cliente);
        $this->entorno($cliente, 'https://en-la-otra.test', $otra);

        $this->llamar($licencia, 'https://en-la-otra.test')
            ->assertUnauthorized()
            ->assertJsonPath('error', fn ($error) => str_contains($error, 'belongs to a different licence'));
    }

    public function test_no_se_confirma_el_dominio_de_otro_cliente(): void
    {
        // Si se dijera, esta respuesta sería un comprobador de qué dominios están dados
        // de alta en el Manager, para cualquiera con un token válido.
        $cliente = $this->cliente('Cliente C');
        $licencia = $this->licencia($cliente);

        $ajeno = $this->cliente('Cliente D');
        $this->entorno($ajeno, 'https://de-otro.test', $this->licencia($ajeno));

        $respuesta = $this->llamar($licencia, 'https://de-otro.test')->assertUnauthorized();

        $error = $respuesta->json('error');

        $this->assertStringNotContainsString('licence', str_replace('Environment not found', '', $error));
        $this->assertStringContainsString('Environment not found for this host', $error);
    }

    public function test_un_dominio_que_no_existe_da_el_mensaje_de_siempre(): void
    {
        $cliente = $this->cliente('Cliente E');
        $licencia = $this->licencia($cliente);

        $respuesta = $this->llamar($licencia, 'https://no-existe-en-ningun-sitio.test')->assertUnauthorized();

        $this->assertSame(
            'Environment not found for this host. Host searched: https://no-existe-en-ningun-sitio.test',
            $respuesta->json('error')
        );
    }

    public function test_el_motivo_no_depende_de_como_se_escriba_el_dominio(): void
    {
        // Se compara normalizado, como hace la búsqueda: si se comparara la columna en
        // crudo, un dominio guardado con otra caja daría el mensaje genérico justo
        // cuando más ayuda.
        $cliente = $this->cliente('Cliente F');
        $licencia = $this->licencia($cliente);
        $this->entorno($cliente, 'https://Mayusculas.test');

        $this->llamar($licencia, 'https://mayusculas.test/')
            ->assertUnauthorized()
            ->assertJsonPath('error', fn ($error) => str_contains($error, 'has no licence assigned yet'));
    }
}
