<?php

namespace App\Support;

/**
 * Los tres estados en los que puede estar una versión de contenido (MGR-023).
 *
 * **Es una clase y no parte del trait a propósito.** Los estados son un catálogo de valores
 * —los leen las pantallas, las acciones y los tests— y las constantes de un trait solo se
 * pueden leer desde la clase que lo usa: `TieneEstadoDePublicacion::BORRADOR` es un error de
 * PHP. Puestos aquí, `EstadoDePublicacion::BORRADOR` vale en cualquier sitio, y el trait se
 * queda con lo que es suyo: el comportamiento.
 *
 * ## Qué significa cada uno para el cliente
 *
 * - **Borrador**: se está escribiendo y **no se sirve**, aunque su número le cuadre a media
 *   flota. Es lo que faltaba — preparar una versión lleva días y varias manos, y hasta ahora
 *   la única forma de no publicar a medias era no crear la versión.
 * - **Publicada**: se sirve a quien le corresponda por su número. Es el estado en el que ha
 *   quedado todo el contenido que ya estaba cargado.
 * - **Deshabilitada**: deja de servirse y **la fila se conserva** — es el histórico de qué se
 *   publicó. Quien la recibía pasa a recibir la anterior que le cuadre. Se llama así y no
 *   «retirada» porque retirar suena a que se va, y no se va nada: el contenido sigue ahí y
 *   se vuelve a habilitar cuando se quiera.
 *
 * ## Por qué borrador y retirada, si para la API son lo mismo
 *
 * Las dos dejan de servirse, sí. La diferencia no es para la API: **es para quien mira la
 * pantalla**. Un borrador es *todavía no ha salido* —hay trabajo a medias, alguien tiene que
 * terminarlo—; una retirada es *ya salió y se ha quitado* —es histórico, no hay nada que
 * hacer con ella—. Metidas en un solo estado, la lista de un producto mezcla lo que está
 * pendiente con lo que está jubilado, y eso es justo lo que un administrador entra a
 * distinguir.
 *
 * Lo que sí sobra es **que alguien tenga que elegir entre los dos**. Por eso el camino es
 * uno solo y lo marca {@see self::ACCION_UNICA}: una versión nace en borrador y se publica;
 * una publicada se deshabilita; una deshabilitada se vuelve a habilitar. Nunca se «pasa a
 * borrador» algo que ya salió, porque eso es deshabilitarlo y se llama así.
 */
final class EstadoDePublicacion
{
    public const BORRADOR = 'draft';

    public const PUBLICADA = 'active';

    public const DESHABILITADA = 'deprecated';

    /** Los tres, para validar lo que llega del navegador. */
    public const TODOS = [self::BORRADOR, self::PUBLICADA, self::DESHABILITADA];

    /** Cómo se llama cada uno en la pantalla. */
    public const ETIQUETAS = [
        self::BORRADOR => 'Borrador',
        self::PUBLICADA => 'Publicada',
        self::DESHABILITADA => 'Deshabilitada',
    ];

    /**
     * Lo que cambia para el cliente en cada estado.
     *
     * Está aquí porque es lo que hay que leer **antes** de cambiarlo: el mensaje que
     * acompaña al cambio sale de este mismo sitio, así que la pantalla y la confirmación no
     * pueden decir cosas distintas.
     */
    public const CONSECUENCIAS = [
        self::BORRADOR => 'No se sirve a nadie, aunque su número le cuadre. Sirve para preparar una versión sin publicarla a medias.',
        self::PUBLICADA => 'Se sirve a todos los entornos cuya versión de plugin sea igual o mayor, y que no tengan otra más alta que les cuadre.',
        self::DESHABILITADA => 'Deja de servirse y la fila se conserva, con su contenido. Quien la recibía pasará a recibir la anterior que le cuadre, y se puede volver a habilitar cuando se quiera.',
    ];

    /**
     * El único camino que se ofrece desde cada estado: `[estado al que va, texto del botón]`.
     *
     * **Un botón por fila y no tres.** Con «Publicar», «Pasar a borrador» y «Retirar» a la
     * vez hay que elegir entre dos formas de dejar de servir algo que para el cliente son
     * idénticas, y encima cada pantalla lo llamaba de una manera —los setups decían
     * «deprecar»—. Así el estado al que se va lo decide de dónde se viene, que es la única
     * regla que hace falta saber.
     *
     * Los puntos suspensivos son literales y significan lo que significan en cualquier menú:
     * **esto abre algo antes de hacerlo**. Ninguno de estos botones aplica nada por sí solo;
     * todos abren la modal que cuenta el impacto.
     */
    public const ACCION_UNICA = [
        self::BORRADOR => [self::PUBLICADA, 'Publicar…', 'publicar'],
        self::PUBLICADA => [self::DESHABILITADA, 'Deshabilitar…', 'deshabilitar'],
        self::DESHABILITADA => [self::PUBLICADA, 'Volver a habilitar…', 'publicar'],
    ];

    /**
     * A qué estado lleva el botón de una versión que está en `$actual`.
     *
     * @return array{0: string, 1: string, 2: string} el estado destino, el texto del botón y
     *                                                el nombre de su icono
     */
    public static function accionDesde(?string $actual): array
    {
        return self::ACCION_UNICA[$actual] ?? self::ACCION_UNICA[self::BORRADOR];
    }

    /** Con reserva por si algún día hay una fila con un estado que no conocemos. */
    public static function etiqueta(?string $estado): string
    {
        return self::ETIQUETAS[$estado] ?? (string) $estado;
    }

    public static function consecuencia(?string $estado): string
    {
        return self::CONSECUENCIAS[$estado] ?? '';
    }

    public static function existe(?string $estado): bool
    {
        return in_array($estado, self::TODOS, true);
    }
}
