<?php

namespace Tests\Feature\Environments;

use App\Models\Clients\Client;
use App\Models\Environments\Environment;
use App\Models\Support\EnvironmentSupport;
use App\Models\Support\Support;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;

/**
 * El coste de los atributos de soporte del entorno (MGR-015).
 *
 * **Lo que pasaba.** `Environment::$appends` declaraba ocho atributos de soporte, y los
 * ocho llamaban a `get_current_support()`, que hacía **dos consultas sin memoizar**:
 * 16 consultas para serializar un entorno, en cada `toArray()`, `toJson()` o respuesta de
 * API, **los pidiera alguien o no**. Y no los pide nadie: ninguna pantalla los pinta.
 *
 * Medido antes del arreglo: 16 consultas por entorno con soporte y 50 para los 21 de la
 * base local. Solo 50 en vez de 336 porque el puntero `environment_support_id` estaba a
 * NULL en 19 de ellos —o sea que el coste real aparecía justo al arreglar ese otro bug
 * (MGR-045)—.
 *
 * Este fichero cuenta consultas a propósito. Es un test frágil por naturaleza, y aun así
 * es el único que detecta una regresión de este tipo: volver a meter un atributo en
 * `$appends` no rompe nada visible, solo multiplica las consultas en silencio.
 */
class SupportAttributesTest extends TestCase
{
    use RefreshDatabase;

    private function entornoConSoporte(string $sufijo = 'a'): Environment
    {
        $cliente = Client::create([
            'name' => 'Cliente Soporte ' . $sufijo,
            'shortname' => 'sop' . $sufijo . uniqid(),
            'actived' => true,
        ]);

        $entorno = Environment::create([
            'client_id' => $cliente->id,
            'name' => 'Entorno ' . $sufijo,
            'domain' => 'https://' . uniqid() . '.test',
            'version' => '5.1.1',
            'env' => 'pro',
            'active' => true,
        ]);

        $tipo = Support::firstOrCreate(
            ['shortname' => 'premium'],
            ['name' => 'Soporte Premium', 'description' => 'de prueba']
        );

        EnvironmentSupport::create([
            'environment_id' => $entorno->id,
            'support_id' => $tipo->id,
            'start_at' => now()->subMonths(6)->format('Y-m-d'),
            'end_at' => now()->addMonths(6)->format('Y-m-d'),
        ]);

        // El observador del modelo ha puesto ya el puntero al contrato vigente.
        return $entorno->fresh();
    }

    /** @return array{0: mixed, 1: int} */
    private function contando(callable $accion): array
    {
        DB::flushQueryLog();
        DB::enableQueryLog();

        $resultado = $accion();
        $consultas = count(DB::getQueryLog());

        DB::disableQueryLog();
        DB::flushQueryLog();

        return [$resultado, $consultas];
    }

    public function test_serializar_un_entorno_no_consulta_el_soporte(): void
    {
        // Antes: 16 consultas. Los atributos ya no están en `$appends`, así que quien no
        // los pide no los paga.
        $entorno = $this->entornoConSoporte();

        [, $consultas] = $this->contando(fn () => $entorno->toArray());

        $this->assertSame(0, $consultas);
    }

    public function test_los_atributos_de_soporte_siguen_funcionando(): void
    {
        // Sacarlos de `$appends` no los elimina: quien los pide, los tiene.
        $entorno = $this->entornoConSoporte();

        $this->assertSame('Soporte Premium', $entorno->supportname);
        // 5 = Activado. Y el signo correcto, que es lo que fallaba en MGR-043.
        $this->assertSame(5, $entorno->supportstatus);
        $this->assertGreaterThan(0, $entorno->supportdays);
        $this->assertNotSame('', $entorno->supportend);
    }

    public function test_los_ocho_atributos_juntos_cuestan_dos_consultas(): void
    {
        // Antes eran 16: dos por accessor, porque no se memoizaba nada. Ahora son las
        // dos de cargar la relación, una sola vez.
        $entorno = $this->entornoConSoporte();

        [, $consultas] = $this->contando(function () use ($entorno) {
            return [
                $entorno->supportname,
                $entorno->supporttype,
                $entorno->supportdays,
                $entorno->supporttotal,
                $entorno->supportstart,
                $entorno->supportend,
                $entorno->supportcomment,
                $entorno->supportstatus,
            ];
        });

        $this->assertLessThanOrEqual(2, $consultas);
    }

    public function test_el_soporte_se_puede_precargar_para_todo_un_listado(): void
    {
        // Es lo que no permitía el `DB::table()` suelto: cada entorno hacía su consulta.
        // Con la relación, un listado entero son las mismas 3 consultas dé igual cuántos
        // entornos haya.
        foreach (['a', 'b', 'c'] as $sufijo) {
            $this->entornoConSoporte($sufijo);
        }

        [$lista, $consultas] = $this->contando(function () {
            $lista = Environment::with('soporteVigente.support')->get();

            foreach ($lista as $entorno) {
                $entorno->supportname;
                $entorno->supportstatus;
            }

            return $lista;
        });

        $this->assertCount(3, $lista);
        // 1 del listado + 2 de la precarga.
        $this->assertSame(3, $consultas);
    }

    public function test_un_entorno_sin_soporte_no_consulta_nada(): void
    {
        $cliente = Client::create([
            'name' => 'Cliente sin soporte',
            'shortname' => 'nosop' . uniqid(),
            'actived' => true,
        ]);

        $entorno = Environment::create([
            'client_id' => $cliente->id,
            'name' => 'Sin soporte',
            'domain' => 'https://' . uniqid() . '.test',
            'version' => '5.1.1',
            'env' => 'pro',
            'active' => true,
        ]);

        [, $consultas] = $this->contando(function () use ($entorno) {
            $entorno->toArray();

            return $entorno->supportname;
        });

        $this->assertSame(0, $consultas);
        $this->assertSame('', $entorno->supportname);
    }

    public function test_una_fecha_con_hora_no_revienta(): void
    {
        // `Carbon::createFromFormat('Y-m-d', $valor)` lanza excepción si el valor trae
        // hora, y `end_at` es `date` en MariaDB pero TEXT en sqlite: cualquiera que
        // guarde un datetime ahí —un seeder, una importación, un `attach` con un objeto
        // Carbon— hacía reventar la ficha del entorno. Ahora se parsea con `parse`.
        // Parte de MGR-015.
        $entorno = $this->entornoConSoporte();

        DB::table('environment_support')
            ->where('id', $entorno->environment_support_id)
            ->update(['end_at' => now()->addMonths(6)->format('Y-m-d H:i:s')]);

        $entorno = $entorno->fresh();

        $this->assertSame('Soporte Premium', $entorno->supportname);
        $this->assertGreaterThan(0, $entorno->supportdays);
    }
}
