<?php

namespace App\Services\Stampa;

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

class CreaScontrino
{
    private const LINE_WIDTH = 42;
    private const COL_QTA_WIDTH = 4;
    private const COL_TOTAL_WIDTH = 10;

    /**
     * Genera il buffer ESC/POS e restituisce il payload per print_job (agent: base64).
     */
    public function perOrdine(Ordine $ordine): array
    {
        $ordine->loadMissing(['righe_ordine.piatto', 'attivita', 'metodo_pagamento', 'dispositivo', 'righe_ordine.has_variante.variante_piatto']);

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

        $nomeAttivita = $ordine->attivita?->nome ?? 'Ordine';
        $dataOra = $ordine->created_at?->format('d/m/Y H:i') ?? now()->format('d/m/Y H:i');

        $totale = $this->calcolaTotale($ordine);
        $this->printHeader($printer, $ordine, $nomeAttivita);

        $info = is_array($ordine->info) ? $ordine->info : [];
        $cliente = $info['cliente'] ?? null;
        $tavolo = $info['tavolo'] ?? null;
        if ($cliente) {
            $printer->text("Cliente:\n");
            $printer->setEmphasis(true);
            $printer->setTextSize(2, 2);
            $printer->text(mb_strtoupper($this->sanitizeLine((string) $cliente))."\n");
            $printer->setTextSize(1, 1);
            $printer->setEmphasis(false);
        }
        if ($tavolo) {
            $printer->text("Tavolo:\n");
            $printer->setEmphasis(true);
            $printer->setTextSize(2, 2);
            $printer->text(mb_strtoupper($this->sanitizeLine((string) $tavolo))."\n");
            $printer->setTextSize(1, 1);
            $printer->setEmphasis(false);
        }
        if (! empty($ordine->note)) {
            $printer->text('Note: '.$this->sanitizeLine((string) $ordine->note)."\n");
        }

        $printer->text($this->separator());
        $this->printItemsTable($printer, $ordine);

        $this->printTotals($printer, $ordine, $totale, $dataOra);
        $this->printFooter($printer, $ordine);

        $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,
            'ordine_totale' => (float) $totale,
        ];
    }

    private function printHeader(Printer $printer, Ordine $ordine, string $nomeAttivita): void
    {
        $nomeAttivitaHeader = mb_strtoupper($this->sanitizeLine($nomeAttivita));

        $printer->setJustification(Printer::JUSTIFY_CENTER);
        $printer->setEmphasis(true);
        $printer->setTextSize(2, 2);
        $printer->text($nomeAttivitaHeader."\n");
        $printer->setTextSize(1, 1);
        $printer->setEmphasis(false);
        $printer->text("\n");
        $printer->setJustification(Printer::JUSTIFY_CENTER);
        $printer->setEmphasis(true);
        $printer->setTextSize(2, 2);
        $printer->text("#".$ordine->id."\n");
        $printer->setTextSize(1, 1);
        $printer->setEmphasis(false);
        $printer->setJustification(Printer::JUSTIFY_LEFT);
    }

    private function printItemsTable(Printer $printer, Ordine $ordine): void
    {
        $itemWidth = $this->itemColumnWidth();
        $printer->setEmphasis(true);
        $printer->text(
            str_pad('QTA', self::COL_QTA_WIDTH).
            str_pad('PIATTO', $itemWidth).
            str_pad('TOT', self::COL_TOTAL_WIDTH, ' ', STR_PAD_LEFT)."\n"
        );
        $printer->setEmphasis(false);

        foreach ($ordine->righe_ordine as $riga) {
            $nome = $riga->piatto?->nome ?? 'Voce';
            $q = max(1, (int) ($riga->quantita ?? 1));
            $lineTotal = (float) ($riga->piatto->prezzo*$q ?? 0);
            // $lineTotal = (float) ($riga->prezzo ?? 0);
            $amount = $this->moneyShort($lineTotal);
            $nameLines = $this->wrapText($this->sanitizeLine($nome), $itemWidth);

            $firstLine = $nameLines[0] ?? '';
            $printer->text(
                str_pad((string) $q, self::COL_QTA_WIDTH).
                str_pad($firstLine, $itemWidth).
                str_pad($amount, self::COL_TOTAL_WIDTH, ' ', STR_PAD_LEFT)."\n"
            );

            for ($i = 1; $i < count($nameLines); $i++) {
                $printer->text(
                    str_repeat(' ', self::COL_QTA_WIDTH).
                    str_pad($nameLines[$i], $itemWidth)."\n"
                );
            }

            if (! empty($riga->note)) {
                $note = 'Note: '.$this->sanitizeLine((string) $riga->note);
                foreach ($this->wrapText($note, self::LINE_WIDTH - self::COL_QTA_WIDTH) as $noteLine) {
                    $printer->text(str_repeat(' ', self::COL_QTA_WIDTH).$noteLine."\n");
                }
            }
            // Stampa le varianti nello stesso formato delle voci dei piatti (con wrapping)
            if($riga->has_variante->isNotEmpty()){
                foreach($riga->has_variante as $variante){
                    $label = ">> " . $variante->variante_piatto->label;
                    $prezzo = $this->moneyShort($variante->variante_piatto->prezzo ?? 0);
                    $varianteLines = $this->wrapText($this->sanitizeLine($label), $itemWidth);
                    $firstVarianteLine = $varianteLines[0] ?? '';
                    $printer->text(
                        str_repeat(' ', self::COL_QTA_WIDTH) .
                        str_pad($firstVarianteLine, $itemWidth) .
                        str_pad($prezzo, self::COL_TOTAL_WIDTH, ' ', STR_PAD_LEFT) . "\n"
                    );
                    for ($j = 1; $j < count($varianteLines); $j++) {
                        $printer->text(
                            str_repeat(' ', self::COL_QTA_WIDTH) .
                            str_pad($varianteLines[$j], $itemWidth) . "\n"
                        );
                    }
                }
            }
    
        }
    }

    private function printTotals(Printer $printer, Ordine $ordine, float $totale, string $dataOra): void
    {
        $printer->text($this->separator());
        $printer->setEmphasis(true);
        $printer->setTextSize(2, 2);
        $printer->text(
            str_pad('TOTALE', self::LINE_WIDTH - self::COL_TOTAL_WIDTH) .
            str_pad($this->money($totale), self::COL_TOTAL_WIDTH, ' ', STR_PAD_LEFT) . "\n"
        );
        $printer->setTextSize(1, 1);
        $printer->setEmphasis(false);
        $printer->text("\n");
        $printer->text("Da:".$ordine->dispositivo?->nome ?? 'ND');
        $printer->text("\n");
        $metodoPagamento = $ordine->pagamento?->metodo_pagamento?->tipo ?? 'ND';
        $printer->text($this->pairLine(mb_strtoupper($this->sanitizeLine((string) $metodoPagamento)), $this->sanitizeLine($dataOra)));
    }

    private function printFooter(Printer $printer, Ordine $ordine): void
    {
        $printer->text($this->separator());
        $printer->setJustification(Printer::JUSTIFY_CENTER);
        if(Impostazioni::getImpostazione('stampa_qr_code_chiamata')){
        $printer->qrCode(route('consulta.scontrino.show', ['ordine_id' => $ordine->id]) , Printer::QR_ECLEVEL_L, 6, Printer::QR_MODEL_2);
        // $this->printQrCode($printer, route('consulta.scontrino.show', ['ordine_id' => $ordine->id]));
        }
        $printer->text("\n");
        $printer->text("Grazie e buon appetito!\n\n");
        $printer->setEmphasis(false);
        $printer->setFont(Printer::FONT_B);
        $printer->text("SegrestaFest\n");
        $printer->text("Made by Elephantech.it\n");
        $printer->setFont(Printer::FONT_A);
        $printer->setJustification(Printer::JUSTIFY_LEFT);
    }

    private function calcolaTotale(Ordine $ordine): float
    {
        $totale = $ordine->prezzo;
        if ($totale === null || (float) $totale === 0.0) {
            $totale = $ordine->righe_ordine->sum('prezzo');
        }

        return (float) $totale;
    }

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

    private function money(float $value): string
    {
        return number_format($value, 2, ',', '.').' EUR';
    }

    private function moneyShort(float $value): string
    {
        return number_format($value, 2, ',', '.');
    }

    private function pairLine(string $left, string $right): string
    {
        $left = $this->sanitizeLine($left);
        $right = $this->sanitizeLine($right);
        $space = self::LINE_WIDTH - mb_strlen($left) - mb_strlen($right);
        if ($space < 1) {
            return $left."\n".$right."\n";
        }

        return $left.str_repeat(' ', $space).$right."\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;
    }

    // private function printQrCode(Printer $printer, string $value): void
    // {
    //     try {
    //         $printer->qrCode($this->sanitizeLine($value), Printer::QR_ECLEVEL_L, 6, Printer::QR_MODEL_2);
    //     } catch (\Throwable $e) {
    //         $printer->text('[QR: '.$this->sanitizeLine($value)."]\n");
    //     }
    // }

    private function itemColumnWidth(): int
    {
        return self::LINE_WIDTH - self::COL_QTA_WIDTH - self::COL_TOTAL_WIDTH;
    }

    /**
     * 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);
    }
}
