<?php

namespace App\Services\Bilancio;

use App\Models\ChiusuraServizio;
use App\Models\Pagamento;
use App\Models\RigaOrdine;
use Carbon\Carbon;

class BilancioServizioService
{
    /**
     * Inizio del servizio corrente: ultima chiusura registrata, altrimenti inizio giornata.
     */
    public function periodStartForAttivita(int $attivitaId, ?Carbon $reference = null): Carbon
    {
        $lastChiusura = $this->lastChiusuraAt($attivitaId);

        if ($lastChiusura) {
            return $lastChiusura->copy();
        }

        return ($reference ?? now())->copy()->startOfDay();
    }

    public function lastChiusuraAt(int $attivitaId): ?Carbon
    {
        $ultimaChiusura = ChiusuraServizio::query()
            ->where('attivita_id', $attivitaId)
            ->orderByDesc('chiusura_at')
            ->value('chiusura_at');

        return $ultimaChiusura ? Carbon::parse($ultimaChiusura) : null;
    }

    /**
     * @return array{start: Carbon, end: Carbon, ultimaChiusuraAt: ?Carbon}
     */
    public function currentPeriod(int $attivitaId, ?Carbon $reference = null): array
    {
        $end = ($reference ?? now())->copy();
        $ultimaChiusuraAt = $this->lastChiusuraAt($attivitaId);
        $start = $ultimaChiusuraAt
            ? $ultimaChiusuraAt->copy()
            : $end->copy()->startOfDay();

        return [
            'start' => $start,
            'end' => $end,
            'ultimaChiusuraAt' => $ultimaChiusuraAt,
        ];
    }

    /**
     * Totali e breakdown per un intervallo temporale (stessa logica di bilancio servizio).
     */
    public function forPeriod(int $attivitaId, Carbon $start, Carbon $end): array
    {
        $righeOrdini = RigaOrdine::query()
            ->with(['piatto.cucina', 'ordine'])
            ->whereHas('ordine', function ($query) use ($attivitaId, $start, $end) {
                $query
                    ->where('attivita_id', $attivitaId)
                    ->whereBetween('created_at', [$start, $end]);
            })
            ->get();

        $righeIncassi = RigaOrdine::query()
            ->with(['piatto.cucina', 'ordine'])
            ->whereHas('ordine.pagamenti', function ($query) use ($attivitaId, $start, $end) {
                $query
                    ->where('attivita_id', $attivitaId)
                    ->whereBetween('created_at', [$start, $end]);
            })
            ->get();

        $righe = $righeOrdini
            ->concat($righeIncassi)
            ->unique('id')
            ->values();

        $cucineRows = $righe
            ->groupBy(fn ($riga) => $riga->piatto?->cucina_id ?: 0)
            ->map(function ($items, $cucinaId) {
                $first = $items->first();
                $cucinaNome = $first?->piatto?->cucina?->nome ?? ('Cucina #' . $cucinaId);

                $piatti = $items
                    ->groupBy('piatto_id')
                    ->map(function ($piattoItems) {
                        $firstPiatto = $piattoItems->first();
                        $quantita = (int) $piattoItems->sum(fn ($r) => (int) ($r->quantita ?? 0));
                        $totale = (float) $piattoItems->sum(fn ($r) => ((float) ($r->quantita ?? 0)) * ((float) ($r->prezzo ?? 0)));

                        return [
                            'nome' => $firstPiatto?->piatto?->nome ?? 'Piatto',
                            'quantita' => $quantita,
                            'totale' => $totale,
                        ];
                    })
                    ->sortByDesc('quantita')
                    ->values();

                return [
                    'id' => (int) $cucinaId,
                    'nome' => $cucinaNome,
                    'piatti' => $piatti,
                    'tot_quantita' => (int) $piatti->sum('quantita'),
                    'tot_importo' => (float) $piatti->sum('totale'),
                ];
            })
            ->sortByDesc('tot_quantita')
            ->values();

        $pagamenti = Pagamento::query()
            ->with(['metodo_pagamento', 'ordine.righe_ordine.piatto.cucina'])
            ->where('attivita_id', $attivitaId)
            ->whereBetween('created_at', [$start, $end])
            ->get();

        $paymentSummary = $pagamenti
            ->groupBy(fn ($pagamento) => $pagamento->metodo_pagamento?->nome
                ?? $pagamento->metodo_pagamento?->tipo
                ?? 'N/D')
            ->map(function ($items, $metodo) {
                return [
                    'metodo' => (string) $metodo,
                    'count' => $items->count(),
                    'totale' => (float) $items->sum(fn ($p) => (float) ($p->importo ?? 0)),
                ];
            })
            ->sortByDesc('totale')
            ->values();

        $totaleIncasso = (float) $pagamenti->sum(fn ($p) => (float) ($p->importo ?? 0));
        $totaleTransazioni = $pagamenti->count();
        $totPiattiOrdinati = (int) $cucineRows->sum('tot_quantita');

        $hourLabels = collect(range(0, 23))->map(fn ($h) => str_pad((string) $h, 2, '0', STR_PAD_LEFT))->values();

        $heatmapSeries = $cucineRows->map(function ($cucinaRow) use ($pagamenti) {
            $cucinaId = (int) ($cucinaRow['id'] ?? 0);
            $data = [];
            foreach (range(0, 23) as $h) {
                $hourTotal = (int) $pagamenti
                    ->filter(fn ($pagamento) => !empty($pagamento->created_at) && (int) $pagamento->created_at->format('H') === $h)
                    ->sum(function ($pagamento) use ($cucinaId) {
                        if (empty($pagamento->ordine)) {
                            return 0;
                        }

                        return (int) $pagamento->ordine->righe_ordine->sum(function ($riga) use ($cucinaId) {
                            if ((int) ($riga->piatto?->cucina_id ?? 0) !== $cucinaId) {
                                return 0;
                            }

                            return (int) ($riga->quantita ?? 0);
                        });
                    });
                $data[] = $hourTotal;
            }

            return [
                'name' => (string) ($cucinaRow['nome'] ?? 'Cucina'),
                'data' => $data,
            ];
        })->values();

        return [
            'periodStart' => $start,
            'periodEnd' => $end,
            'cucineRows' => $cucineRows,
            'paymentSummary' => $paymentSummary,
            'totaleIncasso' => $totaleIncasso,
            'totaleTransazioni' => $totaleTransazioni,
            'totPiattiOrdinati' => $totPiattiOrdinati,
            'heatmapHourLabels' => $hourLabels,
            'heatmapSeries' => $heatmapSeries,
        ];
    }

    /**
     * Snapshot JSON da salvare in chiusura_servizio.info (audit del segmento chiuso).
     */
    public function buildSnapshot(int $attivitaId, Carbon $start, Carbon $end): array
    {
        $report = $this->forPeriod($attivitaId, $start, $end);

        return [
            'periodo' => [
                'da' => $start->toIso8601String(),
                'a' => $end->toIso8601String(),
            ],
            'incasso' => $report['totaleIncasso'],
            'transazioni' => $report['totaleTransazioni'],
            'piatti_ordinati' => $report['totPiattiOrdinati'],
            'pagamenti_per_metodo' => $report['paymentSummary']->values()->all(),
            'cucine' => $report['cucineRows']->values()->map(function ($cucina, $index) use ($report) {
                $orario = $report['heatmapSeries']->get($index);

                return [
                    'nome' => $cucina['nome'],
                    'tot_quantita' => $cucina['tot_quantita'],
                    'tot_importo' => $cucina['tot_importo'],
                    'piatti' => $cucina['piatti']->values()->all(),
                    'ordini_per_ora' => $orario['data'] ?? [],
                ];
            })->all(),
            'heatmap_hour_labels' => $report['heatmapHourLabels']->values()->all(),
            'heatmap_series' => $report['heatmapSeries']->values()->all(),
            'generato_at' => now()->toIso8601String(),
        ];
    }

    /**
     * Arricchisce le cucine dello snapshot con ordini orari (fallback da heatmap_series).
     */
    public function cucineWithOrario(array $snapshot, $heatmapSeries): \Illuminate\Support\Collection
    {
        $series = collect($heatmapSeries);

        return collect($snapshot['cucine'] ?? [])->values()->map(function ($cucina, $index) use ($series) {
            if (!empty($cucina['ordini_per_ora'])) {
                return $cucina;
            }

            $orario = $series->get($index) ?? $series->firstWhere('name', $cucina['nome'] ?? '');
            $cucina['ordini_per_ora'] = $orario['data'] ?? [];

            return $cucina;
        });
    }

    /**
     * Heatmap dallo snapshot; per chiusure precedenti senza dati, ricalcolo sul periodo.
     *
     * @return array{heatmapHourLabels: \Illuminate\Support\Collection, heatmapSeries: \Illuminate\Support\Collection}
     */
    public function heatmapFromSnapshot(int $attivitaId, array $snapshot, ?Carbon $periodoDa, ?Carbon $periodoA): array
    {
        $series = collect($snapshot['heatmap_series'] ?? []);
        $labels = collect($snapshot['heatmap_hour_labels'] ?? []);

        if ($series->isNotEmpty()) {
            return [
                'heatmapHourLabels' => $labels,
                'heatmapSeries' => $series,
            ];
        }

        if (!$periodoDa || !$periodoA) {
            return [
                'heatmapHourLabels' => collect(),
                'heatmapSeries' => collect(),
            ];
        }

        $report = $this->forPeriod($attivitaId, $periodoDa, $periodoA);

        return [
            'heatmapHourLabels' => $report['heatmapHourLabels'],
            'heatmapSeries' => $report['heatmapSeries'],
        ];
    }

    public function periodoLabel(Carbon $start, Carbon $end, ?Carbon $lastChiusura = null): string
    {
        $range = $start->format('d/m/Y H:i') . ' → ' . $end->format('d/m/Y H:i');

        if ($lastChiusura) {
            return 'Servizio in corso · dal ' . $lastChiusura->format('d/m/Y H:i') . ' · adesso';
        }

        return 'Servizio in corso · ' . $range;
    }
}
