<?php

namespace App\Services;

use App\Models\MasterMenu;
use App\Models\OrdersHeader;

class PrintPayloadBuilder
{
    public function buildFromOrder(OrdersHeader $orderHeader, iterable $orderDetails): array
    {
        $orderDetails = collect($orderDetails);
        $items = [];
        $lines = [];

        $type = $orderHeader->type;
        $typeLabel = $type === 'dine_in'
            ? 'DINE IN'
            : ($type === 'take_away' ? 'TAKE AWAY' : strtoupper((string) $type));
        $meja = $orderHeader->meja?->no_meja;
        $pelanggan = $orderHeader->nama_pelanggan;

        if ($typeLabel) {
            $lines[] = 'Tipe: ' . $typeLabel;
        }
        if ($type === 'dine_in' && $meja) {
            $lines[] = 'Meja: ' . $meja;
        }

        if ($type === 'take_away' && $pelanggan) {
            $lines[] = 'Pelanggan: ' . $pelanggan;
        }

        $waiter = $orderHeader->waiter?->nama;
        if ($waiter) {
            $lines[] = 'Waiter: ' . $waiter;
        }

        $lines[] = '----------------';

        foreach ($orderDetails as $detail) {
            if (isset($detail->status) && $detail->status === 'canceled') {
                continue;
            }

            $menuName = $detail->menu?->nama ?? 'Menu';
            $qty = (int) $detail->qty;
            $price = $detail->harga ?? $detail->menu?->harga_jual ?? 0;
            $standName = $detail->menu?->stand?->nama ?? 'General';
            $typeMenu = $detail->menu?->type_menu ?? null;

            $lines[] = sprintf(
                '%dx %s  %s',
                $qty,
                $menuName,
                format_rupiah($price)
            );

            $note = $detail->note ?? null;
            if ($note) {
                $lines[] = '  Catatan: ' . $note;
            }

            $items[] = [
                'id_menu' => $detail->id_menu,
                'nama' => $menuName,
                'qty' => $qty,
                'harga' => $price,
                'note' => $note,
                'type_menu' => $typeMenu,
                'stand' => $standName,
                'meja' => $meja,
                'pelanggan' => $pelanggan,
                'type' => $type,
                'is_package' => (bool) ($detail->menu?->is_package ?? false),
            ];
        }

        $lines[] = '----------------';
        $lines[] = 'TOTAL: ' . format_rupiah($orderHeader->grand_total ?? 0);

        $standItems = $this->expandStandItemsFromOrder(
            $orderDetails,
            $type,
            $meja,
            $pelanggan
        );

        $standGroups = [];
        foreach ($standItems as $item) {
            $standGroups[$item['stand'] ?? 'General'][] = $item;
        }

        $stands = [];
        foreach ($standGroups as $standName => $standGroupedItems) {
            $standDisplay = $typeLabel ? ($standName . ' (' . $typeLabel . ')') : $standName;
            $standLines = [];

            foreach ($standGroupedItems as $index => $item) {
                if ($index > 0) {
                    $standLines[] = '----------------';
                }
                $standLines[] = sprintf('%dx %s', (int) $item['qty'], $item['nama'] ?? 'Menu');
                if (! empty($item['note'])) {
                    $standLines[] = '  Catatan: ' . $item['note'];
                }
            }

            $stands[] = [
                'stand' => $standDisplay,
                'printer' => $this->resolvePrinterByStand($standName) ?? $this->resolvePrinter([$standGroupedItems[0] ?? []]),
                'lines' => $standLines,
                'items' => $standGroupedItems,
            ];
        }

        $uniqueStands = array_values(array_unique(array_map(
            fn ($item) => (string) ($item['stand'] ?? 'General'),
            $standItems
        )));
        $standSingle = count($uniqueStands) === 1 ? $uniqueStands[0] : null;
        $standDisplaySingle = $standSingle ? ($typeLabel ? ($standSingle . ' (' . $typeLabel . ')') : $standSingle) : null;
        $printer = $standSingle !== null
            ? ($this->resolvePrinterByStand($standSingle) ?? strtolower((string) $standSingle))
            : $this->resolvePrinter($standItems);

        return [
            'printer' => $printer,
            'title' => $typeLabel ?: ('ORDER #' . $orderHeader->id),
            'stand' => $standDisplaySingle ? (string) $standDisplaySingle : null,
            'type' => $type,
            'lines' => $lines,
            'items' => $items,
            'stands' => $stands,
            'footer' => 'Terima kasih',
        ];
    }

    public function buildFromDraft(array $header, array $items): array
    {
        $type = $header['type'] ?? null;
        $typeLabel = $type === 'dine_in'
            ? 'DINE IN'
            : ($type === 'take_away' ? 'TAKE AWAY' : strtoupper((string) $type));
        $meja = $header['meja'] ?? null;
        $pelanggan = $header['nama_pelanggan'] ?? null;

        $lines = [];
        if ($typeLabel) {
            $lines[] = 'Tipe: ' . $typeLabel;
        }
        if ($type === 'dine_in' && $meja) {
            $lines[] = 'Meja: ' . $meja;
        }
        if ($type === 'take_away' && $pelanggan) {
            $lines[] = 'Pelanggan: ' . $pelanggan;
        }

        $waiter = $header['waiter'] ?? null;
        if ($waiter) {
            $lines[] = 'Waiter: ' . $waiter;
        }
        $lines[] = '----------------';

        foreach ($items as $index => $item) {
            if ($index > 0) {
                $lines[] = '----------------';
            }

            $menuName = $item['nama'] ?? 'Menu';
            $qty = (int) ($item['qty'] ?? 1);
            $lines[] = sprintf('%dx %s', $qty, $menuName);

            $note = $item['note'] ?? null;
            if ($note) {
                $lines[] = '  Catatan: ' . $note;
            }
        }

        $standItems = $this->expandStandItemsFromDraft($items, $type, $meja, $pelanggan);

        $standGroups = [];
        foreach ($standItems as $item) {
            $stand = $item['stand'] ?? 'General';
            $standGroups[$stand][] = $item;
        }

        $stands = [];
        foreach ($standGroups as $standName => $standGroupedItems) {
            $standDisplay = $typeLabel ? ($standName . ' (' . $typeLabel . ')') : $standName;
            $standLines = [];

            foreach ($standGroupedItems as $index => $item) {
                if ($index > 0) {
                    $standLines[] = '----------------';
                }

                $menuName = $item['nama'] ?? 'Menu';
                $qty = (int) ($item['qty'] ?? 1);
                $standLines[] = sprintf('%dx %s', $qty, $menuName);

                $note = $item['note'] ?? null;
                if ($note) {
                    $standLines[] = '  Catatan: ' . $note;
                }
            }

            $stands[] = [
                'stand' => $standDisplay,
                'printer' => $this->resolvePrinterByStand($standName) ?? $this->resolvePrinter($standGroupedItems),
                'lines' => $standLines,
                'items' => $standGroupedItems,
            ];
        }

        $uniqueStands = array_values(array_unique(array_keys($standGroups)));
        $standSingle = count($uniqueStands) === 1 ? $uniqueStands[0] : null;
        $standDisplaySingle = $standSingle ? ($typeLabel ? ($standSingle . ' (' . $typeLabel . ')') : $standSingle) : null;
        $printer = $standSingle !== null
            ? ($this->resolvePrinterByStand($standSingle) ?? strtolower((string) $standSingle))
            : $this->resolvePrinter($standItems);

        \Log::info('Print Payload Builder - buildFromDraft', [
            'printer' => $printer,
            'title' => $typeLabel ?: 'ORDER',
            'stand' => $standDisplaySingle ? (string) $standDisplaySingle : null,
            'type' => $type,
            'lines' => $lines,
            'items' => $items,
            'stands' => $stands,
        ]);

        return [
            'printer' => $printer,
            'title' => $typeLabel ?: 'ORDER',
            'stand' => $standDisplaySingle ? (string) $standDisplaySingle : null,
            'type' => $type,
            'lines' => $lines,
            'items' => $items,
            'stands' => $stands,
        ];
    }

    private function expandStandItemsFromOrder(
        iterable $orderDetails,
        ?string $type,
        ?string $meja,
        ?string $pelanggan
    ): array {
        $result = [];

        foreach ($orderDetails as $detail) {
            if (isset($detail->status) && $detail->status === 'canceled') {
                continue;
            }

            $menu = $detail->menu;
            $detailQty = max((int) ($detail->qty ?? 1), 1);
            $note = $detail->note ?? null;

            if (! $menu) {
                continue;
            }

            if ($menu->is_package) {
                $menu->loadMissing('packageComponents.stand');
                $components = $menu->packageComponents;

                if ($components->isNotEmpty()) {
                    foreach ($components as $component) {
                        $componentQty = max((int) ($component->pivot->qty ?? 1), 1);
                        $this->appendStandItem($result, [
                            'id_menu' => $component->id,
                            'nama' => $component->nama,
                            'qty' => $detailQty * $componentQty,
                            'harga' => (float) ($component->harga_jual ?? 0),
                            'note' => $note,
                            'type_menu' => $component->type_menu,
                            'stand' => $component->stand?->nama ?? 'General',
                            'meja' => $meja,
                            'pelanggan' => $pelanggan,
                            'type' => $type,
                            'is_package_component' => true,
                            'source_package' => $menu->nama,
                        ]);
                    }
                    continue;
                }
            }

            $this->appendStandItem($result, [
                'id_menu' => $menu->id,
                'nama' => $menu->nama,
                'qty' => $detailQty,
                'harga' => (float) ($detail->harga ?? $menu->harga_jual ?? 0),
                'note' => $note,
                'type_menu' => $menu->type_menu,
                'stand' => $menu->stand?->nama ?? 'General',
                'meja' => $meja,
                'pelanggan' => $pelanggan,
                'type' => $type,
                'is_package_component' => false,
            ]);
        }

        return array_values($result);
    }

    private function expandStandItemsFromDraft(
        array $items,
        ?string $type,
        ?string $meja,
        ?string $pelanggan
    ): array {
        $result = [];

        $menuIds = collect($items)
            ->pluck('id_menu')
            ->filter(fn ($id) => ! blank($id))
            ->map(fn ($id) => (int) $id)
            ->unique()
            ->values()
            ->all();

        $menus = MasterMenu::query()
            ->with(['stand', 'packageComponents.stand'])
            ->whereIn('id', $menuIds)
            ->get()
            ->keyBy('id');

        foreach ($items as $item) {
            $qty = max((int) ($item['qty'] ?? 1), 1);
            $note = $item['note'] ?? null;
            $menuId = isset($item['id_menu']) ? (int) $item['id_menu'] : null;
            $menu = $menuId ? $menus->get($menuId) : null;

            if ($menu && $menu->is_package && $menu->packageComponents->isNotEmpty()) {
                foreach ($menu->packageComponents as $component) {
                    $componentQty = max((int) ($component->pivot->qty ?? 1), 1);
                    $this->appendStandItem($result, [
                        'id_menu' => $component->id,
                        'nama' => $component->nama,
                        'qty' => $qty * $componentQty,
                        'harga' => (float) ($component->harga_jual ?? 0),
                        'note' => $note,
                        'type_menu' => $component->type_menu,
                        'stand' => $component->stand?->nama ?? 'General',
                        'meja' => $meja,
                        'pelanggan' => $pelanggan,
                        'type' => $type,
                        'is_package_component' => true,
                        'source_package' => $menu->nama,
                    ]);
                }
                continue;
            }

            $standName = $menu?->stand?->nama ?? ($item['stand'] ?? 'General');
            $menuName = $menu?->nama ?? ($item['nama'] ?? 'Menu');
            $price = (float) ($item['harga'] ?? $menu?->harga_jual ?? 0);
            $typeMenu = $menu?->type_menu ?? ($item['type_menu'] ?? null);

            $this->appendStandItem($result, [
                'id_menu' => $menu?->id ?? $menuId,
                'nama' => $menuName,
                'qty' => $qty,
                'harga' => $price,
                'note' => $note,
                'type_menu' => $typeMenu,
                'stand' => $standName,
                'meja' => $meja,
                'pelanggan' => $pelanggan,
                'type' => $type,
                'is_package_component' => false,
            ]);
        }

        return array_values($result);
    }

    private function appendStandItem(array &$items, array $item): void
    {
        $key = implode('|', [
            (string) ($item['stand'] ?? ''),
            (string) ($item['nama'] ?? ''),
            (string) ($item['note'] ?? ''),
            (string) ($item['source_package'] ?? ''),
        ]);

        if (! isset($items[$key])) {
            $items[$key] = $item;
            return;
        }

        $items[$key]['qty'] = ((int) $items[$key]['qty']) + ((int) ($item['qty'] ?? 0));
    }

    private function resolvePrinter(iterable $orderDetails): string
    {
        $mapping = config('printing.type_to_printer', []);
        $default = config('printing.default_printer', 'kitchen');

        $totals = [];

        foreach ($orderDetails as $detail) {
            $type = $this->extractTypeMenu($detail);
            if (! $type) {
                continue;
            }

            $qty = max((int) $this->extractQty($detail), 1);
            $totals[$type] = ($totals[$type] ?? 0) + $qty;
        }

        if ($totals === []) {
            return $default;
        }

        arsort($totals);
        $dominantType = array_key_first($totals);

        return $mapping[$dominantType] ?? $default;
    }

    private function resolvePrinterByStand(?string $standName): ?string
    {
        if (! $standName) {
            return null;
        }
        $key = strtolower(trim($standName));
        if ($key === '') {
            return null;
        }

        $mapping = config('printing.stand_to_printer', []);
        if (isset($mapping[$key])) {
            return $mapping[$key];
        }

        return $key;
    }

    private function extractTypeMenu(mixed $detail): ?string
    {
        if (is_array($detail)) {
            return $detail['type_menu'] ?? ($detail['menu']['type_menu'] ?? null);
        }

        return $detail->menu?->type_menu ?? null;
    }

    private function extractQty(mixed $detail): int
    {
        if (is_array($detail)) {
            return (int) ($detail['qty'] ?? 1);
        }

        return (int) ($detail->qty ?? 1);
    }
}
