<?php

namespace App\Support;

use App\Models\Environments\Environment;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Facades\DB;

/**
 * Cuándo se puede borrar un entorno: **solo si está limpio**.
 *
 * Hasta ahora un entorno no se podía borrar, solo apagar. Pero los que se crean por error
 * —un dominio mal escrito, un `http://`, una prueba— se quedaban para siempre en el listado y
 * ocupando su dominio en el índice único. Ahora se pueden borrar **cuando no tienen nada**:
 * ni licencia ni ninguna fila que cuelgue de ellos. Así borrar no le cambia nada a nadie,
 * y puede ir detrás de un botón.
 *
 * **Las peticiones a la API cuentan.** Un entorno sin licencia pero con peticiones estuvo
 * dado de alta y funcionando, y su historial se quedaría sin entorno: `api_request_logs` y
 * `api_blocks` tienen `nullOnDelete`.
 *
 * **Las tablas se escriben en un solo sitio, aquí**, y las leen los dos caminos: el detalle
 * de una ficha (`motivos()`) y el listado (`subconsultaEstaLimpio()`). Con la lista copiada,
 * el día que se añada una tabla, un camino la miraría y el otro no. Hay un test que recorre la
 * base y falla si aparece una tabla con `environment_id` que no está en `TABLAS`.
 */
class EntornoBorrable
{
    /**
     * Las tablas que cuelgan de un entorno, con cómo se dice en la modal.
     *
     * @var array<string, array{string, string}> tabla => [singular, plural]
     */
    public const TABLAS = [
        'api_request_logs' => ['petición a la API registrada', 'peticiones a la API registradas'],
        'api_blocks' => ['episodio de corte de la API', 'episodios de corte de la API'],
        'plugins' => ['plugin sincronizado', 'plugins sincronizados'],
        'data' => ['ficha de datos de uso', 'fichas de datos de uso'],
        'environment_stats' => ['día de estadísticas', 'días de estadísticas'],
        'environment_support' => ['contrato de soporte', 'contratos de soporte'],
        'reports' => ['informe', 'informes'],
        'updates' => ['actualización registrada', 'actualizaciones registradas'],
    ];

    /**
     * Por qué no se puede borrar, en palabras. Vacío si está limpio.
     *
     * @return list<string>
     */
    public static function motivos(Environment $environment): array
    {
        $motivos = [];

        if ($environment->license_token_id !== null) {
            $motivos[] = 'tiene una licencia asociada';
        }

        foreach (self::TABLAS as $tabla => [$singular, $plural]) {
            $cuantas = DB::table($tabla)->where('environment_id', $environment->id)->count();

            if ($cuantas > 0) {
                $motivos[] = 'tiene ' . $cuantas . ' ' . ($cuantas === 1 ? $singular : $plural);
            }
        }

        return $motivos;
    }

    public static function estaLimpio(Environment $environment): bool
    {
        return self::motivos($environment) === [];
    }

    /**
     * La misma regla como **una** subconsulta, para el listado.
     *
     * El listado pinta el candado en cada fila, y con `motivos()` serían nueve consultas por
     * entorno. Así es una columna más en la consulta que ya hace, que tiene un test que las
     * cuenta. Se usa con `addSelect(['esta_limpio' => EntornoBorrable::subconsultaEstaLimpio()])`.
     */
    public static function subconsultaEstaLimpio(): Builder
    {
        $condiciones = ['environments.license_token_id IS NULL'];

        foreach (array_keys(self::TABLAS) as $tabla) {
            $condiciones[] = "NOT EXISTS (SELECT 1 FROM {$tabla} WHERE {$tabla}.environment_id = environments.id)";
        }

        return DB::query()->selectRaw('CASE WHEN ' . implode(' AND ', $condiciones) . ' THEN 1 ELSE 0 END');
    }
}
