<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Asporto;
use Illuminate\Support\Collection;

class AsportoController extends Controller
{
    public static $permission_group = 'asporto';

    public static $permissions = [
        'view-asporto' => 'Visualizza asporto',
    ];

    public function index(Request $request)
    {

        return view('asporto.index');
    }

    public function dashboard()
    {
        $asportiPreparazione = $this->preparazioneList();
        $asportiChiamata = $this->chiamataList();
        $asportiRitiro = $this->ritiroList();
        $asportiTimeline = $asportiPreparazione
            ->concat($asportiChiamata)
            ->concat($asportiRitiro)
            ->sortBy(function ($asporto) {
                $ora = $asporto->ora_ritiro;
                if ($ora instanceof \DateTimeInterface) {
                    return $ora->format('H:i:s');
                }

                return (string) ($ora ?: '99:99:99');
            })
            ->values();

        return view('asporto.dashboard', compact(
            'asportiPreparazione',
            'asportiChiamata',
            'asportiRitiro',
            'asportiTimeline'
        ));
    }

    public function dashboard_slot(Request $request)
    {
        $impostazioni = \App\Models\Impostazioni::query()
            ->where('attivita_id', session('attivita_attuale'))
            ->whereIn('chiave', [
                'ora_inizio_asporto',
                'ora_fine_asporto',
                'durata_slot_oraria_consegna_asporto',
                'qta_massima_ordini_per_slot_oraria',
            ])
            ->get()
            ->keyBy('chiave');

        $startMin = $this->timeToMinutes($impostazioni->get('ora_inizio_asporto')?->valore ?? '19:00');
        $endMin = $this->timeToMinutes($impostazioni->get('ora_fine_asporto')?->valore ?? '23:00');
        $durata = max(1, (int) ($impostazioni->get('durata_slot_oraria_consegna_asporto')?->valore ?? 15));
        $maxOrdini = max(1, (int) ($impostazioni->get('qta_massima_ordini_per_slot_oraria')?->valore ?? 1));

        $asportiDiOggi = Asporto::query()
            ->select('asporto.*')
            ->join('ordine', 'ordine.id', '=', 'asporto.ordine_id')
            ->where('ordine.attivita_id', session('attivita_attuale'))
            ->whereDate('asporto.ora_ritiro', today())
            ->orderBy('asporto.ora_ritiro')
            ->with(['ordine'])
            ->get();

        $slots = [];
        if ($startMin !== null && $endMin !== null && $endMin > $startMin) {
            for ($t = $startMin; $t + $durata <= $endMin; $t += $durata) {
                $slots[] = ['start' => $t, 'end' => $t + $durata];
            }
        }

        $activeKeys = [];
        foreach ($slots as $slot) {
            $activeKeys[$slot['start']] = true;
        }

        $hourStart = $startMin !== null ? intdiv($startMin, 60) : 0;
        $hourEnd = $endMin !== null
            ? max($hourStart, (int) ceil($endMin / 60) - ($endMin % 60 === 0 ? 1 : 0))
            : $hourStart;
        $hours = range($hourStart, $hourEnd);

        $minuteSlots = [];
        for ($m = 0; $m + $durata <= 60; $m += $durata) {
            $minuteSlots[] = [
                'start' => $m,
                'end' => $m + $durata,
                'label' => sprintf('%02d–%02d', $m, ($m + $durata) >= 60 ? 59 : ($m + $durata)),
            ];
        }

        $occupancy = [];
        foreach ($asportiDiOggi as $asporto) {
            if (!$asporto->ora_ritiro) {
                continue;
            }
            $ora = \Illuminate\Support\Carbon::parse($asporto->ora_ritiro);
            $oraMin = ((int) $ora->format('H')) * 60 + (int) $ora->format('i');
            $slotStart = null;
            foreach ($slots as $slot) {
                if ($oraMin >= $slot['start'] && $oraMin < $slot['end']) {
                    $slotStart = $slot['start'];
                    break;
                }
            }
            if ($slotStart === null) {
                continue;
            }
            $occupancy[$slotStart] = $occupancy[$slotStart] ?? [];
            $occupancy[$slotStart][] = $asporto;
        }

        $capacitaOra = count($minuteSlots) * $maxOrdini;
        $capacitaTotale = count($slots) * $maxOrdini;
        $occupati = $asportiDiOggi->count();
        $liberi = max(0, $capacitaTotale - $occupati);

        return view('asporto.dashboard_slot', compact(
            'asportiDiOggi',
            'hours',
            'minuteSlots',
            'maxOrdini',
            'durata',
            'activeKeys',
            'occupancy',
            'slots',
            'startMin',
            'endMin',
            'capacitaOra',
            'capacitaTotale',
            'occupati',
            'liberi'
        ));
    }

    protected function timeToMinutes(?string $value): ?int
    {
        if (!$value || !preg_match('/^(\d{1,2}):(\d{2})/', trim($value), $m)) {
            return null;
        }
        $h = (int) $m[1];
        $min = (int) $m[2];
        if ($h > 23 || $min > 59) {
            return null;
        }

        return $h * 60 + $min;
    }

    public function preparazioneList()
    {
        return $this->asportiPerStato(Asporto::PREPARAZIONE);
    }

    public function preparazione()
    {
        $data = $this->boardData('preparazione');

        return view('asporto.postazione.preparazione', $data['view'] + [
            'boardSignature' => $data['signature'],
        ]);
    }

    public function completaPreparazione(Request $request, Asporto $asporto)
    {
        $ordine = $asporto->ordine;
        if (!$ordine || (int) $ordine->attivita_id !== (int) session('attivita_attuale')) {
            return response()->json([
                'success' => false,
                'message' => 'Asporto non trovato per questa attività.',
            ], 404);
        }

        if ($asporto->stato !== Asporto::PREPARAZIONE) {
            return response()->json([
                'success' => false,
                'message' => 'L\'ordine non è più in preparazione.',
            ], 422);
        }

        try {
            app(\App\Services\Asporto\AsportoService::class)->avanzaAChiamata($asporto);
        } catch (\Throwable $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        }

        return response()->json([
            'success' => true,
            'message' => 'Ordine passato a chiamata.',
            'data' => [
                'id' => $asporto->id,
                'stato' => $asporto->stato,
            ],
        ]);
    }

    public function chiamataList()
    {
        return $this->asportiPerStato(Asporto::CHIAMATA);
    }

    public function chiamata()
    {
        $data = $this->boardData('chiamata');

        return view('asporto.postazione.chiamata', $data['view'] + [
            'boardSignature' => $data['signature'],
        ]);
    }

    public function completaChiamata(Request $request, Asporto $asporto)
    {
        $ordine = $asporto->ordine;
        if (!$ordine || (int) $ordine->attivita_id !== (int) session('attivita_attuale')) {
            return response()->json([
                'success' => false,
                'message' => 'Asporto non trovato per questa attività.',
            ], 404);
        }

        if ($asporto->stato !== Asporto::CHIAMATA) {
            return response()->json([
                'success' => false,
                'message' => 'L\'ordine non è più in chiamata.',
            ], 422);
        }

        try {
            app(\App\Services\Asporto\AsportoService::class)->avanzaARitiro($asporto);
        } catch (\Throwable $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        }

        return response()->json([
            'success' => true,
            'message' => 'Ordine passato a ritiro.',
            'data' => [
                'id' => $asporto->id,
                'stato' => $asporto->stato,
            ],
        ]);
    }

    public function ritiroList()
    {
        return $this->asportiPerStato(Asporto::RITIRO);
    }

    public function ritiro()
    {
        $data = $this->boardData('ritiro');

        return view('asporto.postazione.ritiro', $data['view'] + [
            'boardSignature' => $data['signature'],
        ]);
    }

    public function completaRitiro(Request $request, Asporto $asporto)
    {
        $ordine = $asporto->ordine;
        if (!$ordine || (int) $ordine->attivita_id !== (int) session('attivita_attuale')) {
            return response()->json([
                'success' => false,
                'message' => 'Asporto non trovato per questa attività.',
            ], 404);
        }

        if ($asporto->stato !== Asporto::RITIRO) {
            return response()->json([
                'success' => false,
                'message' => 'L\'ordine non è più in ritiro.',
            ], 422);
        }

        try {
            app(\App\Services\Asporto\AsportoService::class)->avanzaAConsegnato($asporto);
        } catch (\Throwable $e) {
            return response()->json([
                'success' => false,
                'message' => $e->getMessage(),
            ], 422);
        }

        $asporto = $asporto->fresh('ordine');
        $ordine = $asporto?->ordine;
        $info = is_array($ordine?->info) ? $ordine->info : [];

        return response()->json([
            'success' => true,
            'message' => 'Ordine consegnato.',
            'data' => [
                'id' => $asporto->id,
                'stato' => $asporto->stato,
                'codice' => $ordine?->codice ?? ('A-'.$asporto->ordine_id),
                'cliente' => $info['cliente'] ?? ($ordine?->nome ?? '—'),
                'orario' => $asporto->ora_ritiro
                    ? \Illuminate\Support\Carbon::parse($asporto->ora_ritiro)->format('H:i')
                    : '',
                'consegnato_at' => $asporto->updated_at
                    ? \Illuminate\Support\Carbon::parse($asporto->updated_at)->format('H:i')
                    : '',
                'diff_min' => ($asporto->ora_ritiro && $asporto->updated_at)
                    ? (int) round((
                        \Illuminate\Support\Carbon::parse($asporto->updated_at)->getTimestamp()
                        - \Illuminate\Support\Carbon::parse($asporto->ora_ritiro)->getTimestamp()
                    ) / 60)
                    : null,
            ],
        ]);
    }

    /**
     * Sync fluido postazione: HTML board + signature (no poll se invariato).
     */
    public function sync(Request $request, string $stazione)
    {
        if (! in_array($stazione, ['preparazione', 'chiamata', 'ritiro'], true)) {
            abort(404);
        }

        $data = $this->boardData($stazione);
        $signature = $data['signature'];

        if ((string) $request->query('signature') === $signature) {
            return response()->json([
                'changed' => false,
                'signature' => $signature,
            ]);
        }

        return response()->json([
            'changed' => true,
            'signature' => $signature,
            'html' => view('asporto.postazione._boards.'.$stazione, $data['view'])->render(),
        ]);
    }

    /**
     * @return array{signature: string, view: array<string, mixed>}
     */
    protected function boardData(string $stazione): array
    {
        if ($stazione === 'preparazione') {
            $asporti = $this->preparazioneList();
            $view = [
                'asporti' => $asporti,
                'asportiTimeline' => $asporti->values(),
            ];

            return [
                'signature' => $this->signatureFrom($asporti),
                'view' => $view,
            ];
        }

        if ($stazione === 'chiamata') {
            $asporti = $this->chiamataList();
            $view = [
                'asporti' => $asporti,
                'asportiTimeline' => $asporti->values(),
            ];

            return [
                'signature' => $this->signatureFrom($asporti),
                'view' => $view,
            ];
        }

        $asporti = $this->ritiroList();
        $asportiConsegnati = $this->consegnatiList();
        $asportiTimeline = $this->preparazioneList()
            ->concat($this->chiamataList())
            ->concat($asporti)
            ->concat($asportiConsegnati->take(15))
            ->unique('id')
            ->sortBy(function ($asporto) {
                $ora = $asporto->ora_ritiro;
                if ($ora instanceof \DateTimeInterface) {
                    return $ora->format('H:i:s');
                }

                return (string) ($ora ?: '99:99:99');
            })
            ->values();

        $view = [
            'asporti' => $asporti,
            'asportiConsegnati' => $asportiConsegnati,
            'asportiTimeline' => $asportiTimeline,
        ];

        return [
            'signature' => $this->signatureFrom($asporti->concat($asportiConsegnati)),
            'view' => $view,
        ];
    }

    protected function consegnatiList(): Collection
    {
        return Asporto::query()
            ->with(['ordine'])
            ->where('stato', Asporto::CONSEGNATO)
            ->whereHas('ordine', function ($q) {
                $q->where('attivita_id', session('attivita_attuale'));
            })
            ->orderByDesc('updated_at')
            ->limit(40)
            ->get();
    }

    protected function signatureFrom(Collection $asporti): string
    {
        $parts = $asporti
            ->sortBy('id')
            ->values()
            ->map(function ($asporto) {
                $updated = $asporto->updated_at;
                $ts = $updated instanceof \DateTimeInterface
                    ? $updated->getTimestamp()
                    : (string) $updated;

                return $asporto->id.'|'.$asporto->stato.'|'.$ts;
            })
            ->implode(';');

        return hash('sha256', $parts);
    }

    protected function asportiPerStato(string $stato)
    {
        return Asporto::query()
            ->with(['ordine.righe_ordine.piatto', 'ordine.righe_ordine.has_variante.variante_piatto'])
            ->where('stato', $stato)
            ->whereHas('ordine', function ($q) {
                $q->where('attivita_id', session('attivita_attuale'));
            })
            ->orderBy('ora_ritiro')
            ->get();
    }
}
