<?php

namespace App\Support;

use App\Models\Monitoring\Log as MonitoringLog;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\StreamedResponse;

/**
 * Descargar un listado del panel en CSV (PRODSECU-153).
 *
 * **Las columnas las elige cada listado, una a una.** Nunca el modelo entero: con un
 * `toArray()`, la columna sensible que se añada mañana saldría en la descarga sin que nadie lo
 * decidiera. Lo que no sale nunca —tokens, credenciales, datos personales de contacto, notas
 * libres— está dicho en cada listado, junto a sus columnas.
 *
 * Tres detalles que no se ven y hacen falta:
 *
 * - **Se abre bien en Excel**: BOM de UTF-8, para que los acentos no salgan rotos, y `;` como
 *   separador, que es lo que Excel espera con la configuración regional española.
 * - **Inyección de fórmulas**: el nombre de un entorno, por ejemplo, lo manda el Moodle del
 *   cliente. Una celda que empiece por `=`, `+`, `-`, `@`, tabulador o retorno de carro Excel
 *   la ejecuta como fórmula, así que se le antepone un apóstrofo.
 * - **Queda rastro** de quién descargó qué y con qué filtros: es una salida de datos del panel.
 */
class ExportacionCsv
{
    private const SEPARADOR = ';';

    /**
     * @param  string  $que         el listado, para el nombre del fichero y el rastro: «clientes»
     * @param  list<string>  $cabeceras
     * @param  iterable<list<scalar|null>>  $filas
     * @param  array<string, mixed>  $filtros   los filtros aplicados, para el rastro
     */
    public static function descargar(string $que, array $cabeceras, iterable $filas, array $filtros = []): StreamedResponse
    {
        $activos = array_filter($filtros, fn ($v) => $v !== '' && $v !== null && $v !== false && $v !== []);

        MonitoringLog::db(
            'info',
            '16046',
            'Listado de ' . $que . ' descargado en CSV'
            . ($activos === [] ? ' (sin filtros)' : ' con filtros: ' . json_encode($activos, JSON_UNESCAPED_UNICODE))
            . ' (por ' . (Auth::user()?->name ?? 'desconocido') . ')'
        );

        return response()->streamDownload(function () use ($cabeceras, $filas) {
            $salida = fopen('php://output', 'w');

            fwrite($salida, "\xEF\xBB\xBF");
            fputcsv($salida, $cabeceras, self::SEPARADOR, '"', '');

            foreach ($filas as $fila) {
                fputcsv($salida, array_map([self::class, 'celda'], $fila), self::SEPARADOR, '"', '');
            }

            fclose($salida);
        }, $que . '-' . now()->format('Y-m-d') . '.csv', [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    /** Una celda, neutralizada si Excel la leería como fórmula. */
    public static function celda(mixed $valor): string
    {
        if ($valor === null) {
            return '';
        }

        if (is_bool($valor)) {
            return $valor ? 'Sí' : 'No';
        }

        $texto = (string) $valor;

        // Los números se dejan como están: un `-3` es un número, no una fórmula.
        if (is_int($valor) || is_float($valor)) {
            return $texto;
        }

        return $texto !== '' && in_array($texto[0], ['=', '+', '-', '@', "\t", "\r"], true)
            ? "'" . $texto
            : $texto;
    }
}
