<?php

declare(strict_types=1);

namespace App\Support;

use Illuminate\Contracts\Support\Arrayable;

/**
 * Contrato anti-ceros del dashboard.
 *
 * Toda métrica viaja con su estado, nunca como un número pelado: si el dato no
 * existe o no es confiable, la UI muestra el porqué en vez de un "0" que miente.
 * Ver docs/superpowers/specs/2026-09-08-dashboard-salud-negocio-design.md §C.
 */
final class Metric implements Arrayable
{
    public const STATE_OK = 'ok';

    public const STATE_NO_DATA = 'no_data';

    public const STATE_UNRELIABLE = 'unreliable';

    /** @param array<string, mixed>|null $trend */
    private function __construct(
        public readonly ?float $value,
        public readonly string $state,
        public readonly ?string $note = null,
        public readonly ?array $trend = null,
    ) {}

    /** @param array<string, mixed>|null $trend */
    public static function ok(float $value, ?array $trend = null): self
    {
        return new self($value, self::STATE_OK, null, $trend);
    }

    /** No hay base para calcularlo. La UI dice "sin datos suficientes", nunca 0. */
    public static function noData(string $note): self
    {
        return new self(null, self::STATE_NO_DATA, $note);
    }

    /**
     * El dato existe pero engaña (p. ej. margen sin costos cargados).
     * Se muestra el valor JUNTO con la advertencia.
     *
     * @param  array<string, mixed>|null  $trend
     */
    public static function unreliable(float $value, string $note, ?array $trend = null): self
    {
        return new self($value, self::STATE_UNRELIABLE, $note, $trend);
    }

    /** @return array{value: float|null, state: string, note: string|null, trend: array<string, mixed>|null} */
    public function toArray(): array
    {
        return [
            'value' => $this->value,
            'state' => $this->state,
            'note' => $this->note,
            'trend' => $this->trend,
        ];
    }
}
