<?php

namespace App\Services\Stampa;

use App\Models\Cucina;
use App\Models\Ordine;
use Illuminate\Support\Facades\Log;
use Mike42\Escpos\PrintConnectors\DummyPrintConnector;
use Mike42\Escpos\Printer;

class CreaComandaCucina
{
    /** Larghezza nominale per stampante 78 mm (~48 caratteri font normale). */
    private const LINE_WIDTH = 48;

    private const COL_QTA_WIDTH = 4;

    /**
     * Genera il buffer ESC/POS e restituisce il payload per print_job (agent: base64).
     */
    public function perOrdine(Ordine $ordine, int $cucina_id): array
    {
        $ordine->loadMissing(['righe_ordine.piatto', 'dispositivo', 'righe_ordine.has_variante.variante_piatto']);
        $cucina = Cucina::find($cucina_id);
        if ($cucina === null) {
            Log::error('--- CreaComandaCucina --- Cucina non trovata id: '.$cucina_id);
            throw new \RuntimeException('--- CreaComandaCucina --- Cucina non trovata id: '.$cucina_id);
        }

        $info = is_array($ordine->info) ? $ordine->info : [];
        $dataOra = $ordine->created_at?->format('d/m/Y H:i') ?? now()->format('d/m/Y H:i');

        $connector = new DummyPrintConnector;
        $printer = new Printer($connector);

        $this->printHeader($printer, $ordine, $cucina->nome ?? null, $info);

        $printer->text("\n");
        $printer->text($this->separator());
        $this->printItemsTable($printer, $ordine, $cucina_id);

        $printer->text("\n");
        $this->printFooter($printer, $ordine, $info, $dataOra, $cucina);

        $printer->feed(2);
        $printer->cut();

        // Su DummyPrintConnector, finalize() (chiamata da close()) azzera il buffer: leggere prima i byte.
        $bytes = $connector->getData();
        $printer->close();

        return [
            'formato' => 'escpos_base64',
            'dati' => base64_encode($bytes),
            'ordine_id' => $ordine->id,
            'ordine_numero' => $ordine->id,
            'ordine_data' => $dataOra,
        ];
    }

    private function printHeader(Printer $printer, Ordine $ordine, ?string $nomeCucina, array $info): void
    {
        $printer->setJustification(Printer::JUSTIFY_CENTER);
        $printer->setEmphasis(true);
        $printer->setTextSize(2, 2);
        $printer->text('#'.$ordine->id."\n");
        $printer->setTextSize(1, 1);
        $printer->setEmphasis(false);

        if (! empty($nomeCucina)) {
            $printer->setEmphasis(true);
            $printer->text('Comanda - '.$this->sanitizeLine((string) $nomeCucina)."\n");
            $printer->setEmphasis(false);
        }

        $printer->text("\n");
        $printer->setJustification(Printer::JUSTIFY_LEFT);

        $this->printTavoloClienteRow(
            $printer,
            $info['tavolo'] ?? null,
            $info['cliente'] ?? null
        );

        $this->printExtraInfoFields($printer, $info, $ordine);
    }

    /**
     * Tavolo (col-4) e cliente (col-8) sulla stessa riga, testo grande.
     */
    private function printTavoloClienteRow(Printer $printer, ?string $tavolo, ?string $cliente): void
    {
        $effectiveWidth = (int) (self::LINE_WIDTH / 2);
        $tavoloWidth = (int) floor($effectiveWidth * 4 / 12);
        $clienteWidth = $effectiveWidth - $tavoloWidth;
        $labelTavoloWidth = $tavoloWidth * 2;
        $labelClienteWidth = self::LINE_WIDTH - $labelTavoloWidth;

        $tavoloText = mb_strtoupper($this->sanitizeLine((string) ($tavolo ?? '')));
        $clienteText = mb_strtoupper($this->sanitizeLine((string) ($cliente ?? '')));

        if ($tavoloText === '' && $clienteText === '') {
            return;
        }

        // Label in font normale, più piccola rispetto ai valori 2x2.
        $printer->text(
            str_pad('TAVOLO', $labelTavoloWidth).
            str_pad('CLIENTE', $labelClienteWidth)."\n"
        );

        $tavoloText = mb_substr($tavoloText, 0, $tavoloWidth);
        $clienteText = mb_substr($clienteText, 0, $clienteWidth);

        $printer->setEmphasis(true);
        $printer->setTextSize(2, 2);
        $printer->text(str_pad($tavoloText, $tavoloWidth).str_pad($clienteText, $clienteWidth)."\n");
        $printer->setTextSize(1, 1);
        $printer->setEmphasis(false);
    }

    private function printExtraInfoFields(Printer $printer, array $info, Ordine $ordine): void
    {
        $skip = ['cliente', 'tavolo', 'coperti'];

        foreach ($info as $key => $value) {
            if (in_array($key, $skip, true) || $value === null || $value === '') {
                continue;
            }

            $label = mb_strtoupper(str_replace('_', ' ', (string) $key));
            $printer->text($label.': '.$this->sanitizeLine((string) $value)."\n");
        }

        if (! empty($ordine->note) && empty($info['note_ordine'])) {
            $printer->text('NOTE: '.$this->sanitizeLine((string) $ordine->note)."\n");
        }
    }

    private function printFooter(Printer $printer, Ordine $ordine, array $info, string $dataOra, Cucina $cucina): void
    {
        if($cucina->coperto_come_piatto){
            $coperti = $ordine->righe_ordine->filter(function ($riga) use ($ordine, $cucina) {
                return (int) ($riga->piatto?->cucina_id ?? 0) === $cucina->id;
            });
            $coperti = $coperti->sum('quantita');
            $printer->setTextSize(2, 2);
            $printer->text('Coperti: '.$this->sanitizeLine($coperti)."\n");
        }

        $printer->setTextSize(2, 2);
        $printer->text($this->sanitizeLine($dataOra)."\n");
        $printer->setTextSize(2, 2);
   
        $printer->text($this->sanitizeLine("Op:".(string) ($ordine->dispositivo?->nome ?? 'ND'))."\n");
    }

    private function printItemsTable(Printer $printer, Ordine $ordine, int $cucinaId): void
    {
        $righeCucina = $ordine->righe_ordine->filter(function ($riga) use ($cucinaId) {
            return (int) ($riga->piatto?->cucina_id ?? 0) === $cucinaId;
        });

        if ($righeCucina->isEmpty()) {
            Log::error('--- CreaComandaCucina --- Nessuna riga per cucina id: '.$cucinaId.' ordine: '.$ordine->id);
            throw new \RuntimeException('Nessuna riga ordine trovata per la cucina '.$cucinaId);
        }

        $itemWidth = (int) (self::LINE_WIDTH / 2);

        $printer->setJustification(Printer::JUSTIFY_LEFT);
        foreach ($righeCucina as $riga) {
            $nome = $riga->piatto?->nome ?? 'Voce';
            $q = max(1, (int) ($riga->quantita ?? 1));
            $dishLabel = sprintf('%dx %s', $q, $this->sanitizeLine($nome));
            $nameLines = $this->wrapText($dishLabel, $itemWidth);

            $printer->setEmphasis(true);
            $printer->setTextSize(2, 2);
            foreach ($nameLines as $line) {
                $printer->text($line."\n");
            }
            $printer->setTextSize(1, 1);
            $printer->setEmphasis(false);

            if (! empty($riga->note)) {
                $note = '-> '.$this->sanitizeLine((string) $riga->note);
                $printer->setTextSize(1, 2);
                foreach ($this->wrapText($note, self::LINE_WIDTH - self::COL_QTA_WIDTH) as $noteLine) {
                    $printer->text(str_repeat(' ', self::COL_QTA_WIDTH).$noteLine."\n");
                }
                $printer->setTextSize(1, 1);
            }
            if($riga->has_variante->isNotEmpty()){
                foreach($riga->has_variante as $variante){
                    $printer->setTextSize(1, 2);
                    $printer->text(str_repeat(' ', self::COL_QTA_WIDTH).">> ".$variante->variante_piatto->label."\n");
                    $printer->setTextSize(1, 1);
               
                }
            }

            $printer->text("\n");
        }
    }

    private function separator(): string
    {
        return str_repeat('-', self::LINE_WIDTH)."\n";
    }

    private function wrapText(string $text, int $width): array
    {
        $text = $this->sanitizeLine($text);
        if ($text === '') {
            return [''];
        }

        $parts = [];
        $remaining = $text;
        while (mb_strlen($remaining) > $width) {
            $parts[] = mb_substr($remaining, 0, $width);
            $remaining = mb_substr($remaining, $width);
        }
        $parts[] = $remaining;

        return $parts;
    }

    /**
     * Evita caratteri che alcune stampanti gestiscono male in CP437; raffina dopo con code page UTF-8 se serve.
     */
    private function sanitizeLine(string $line): string
    {
        $line = str_replace(["\r", "\n"], ' ', $line);

        return mb_substr($line, 0, 120);
    }
}
