<?php

namespace App\Http\Controllers;

use App\Models\Attivita;
use App\Models\Cucina;
use App\Models\Ordine;
use App\Models\Pagamento;
use App\Models\PrimaNota;
use App\Models\RigaOrdine;
use Barryvdh\DomPDF\Facade\Pdf;
use Carbon\Carbon;
use Carbon\CarbonPeriod;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;

class ReportController extends Controller
{
    public static $permission_group = "Report";
    public static $permissions = [
        'view-report' => 'Vedi',
        'create-report' => 'Crea',
        'edit-report' => 'Modifica',
        'delete-report' => 'Elimina',
    ];
    
    
    public static function middleware(): array
    {
        return [
            new Middleware('permission:view-report', only: ['index']),
            new Middleware('permission:create-report|edit-report|delete-report', only: ['store', 'update', 'destroy']),
        ];
    }
    

    public function index(Request $request)
    {
        $data = $this->buildReportData($request);

        return view('report.index', $data);
    }

    public function exportPdf(Request $request)
    {
        $data = $this->buildReportData($request);
        $type = strtolower((string) $request->get('type', 'movimenti'));

        $allowedTypes = ['movimenti', 'incassi', 'cucine'];
        if (!in_array($type, $allowedTypes, true)) {
            $type = 'movimenti';
        }

        $pdf = Pdf::loadView('report.pdf', array_merge($data, [
            'reportType' => $type,
        ]))->setPaper('a4', 'portrait');

        $dompdf = $pdf->getDomPDF();
        $dompdf->render();

        $canvas = $dompdf->getCanvas();
        $fontMetrics = $dompdf->getFontMetrics();
        $fontRegular = $fontMetrics->getFont('DejaVu Sans', 'normal');
        $fontBold = $fontMetrics->getFont('DejaVu Sans', 'bold');

        $printedAt = now()->format('d/m/Y H:i');
        $headerText = sprintf(
            '%s - %s',
            (string) ($data['attivitaNome'] ?? 'Attivita'),
            $this->resolveReportPdfTitle($type)
        );

        $canvas->page_script(function ($pageNumber, $pageCount, $canvas, $fontMetrics) use ($fontRegular, $fontBold, $printedAt, $headerText) {
            $width = $canvas->get_width();
            $height = $canvas->get_height();

            // Footer: left print datetime.
            $canvas->text(28, $height - 22, 'Stampa: ' . $printedAt, $fontRegular, 9, [0.35, 0.40, 0.50]);

            // Footer: right page counter.
            $pageText = sprintf('%d / %d', $pageNumber, $pageCount);
            $pageTextWidth = $fontMetrics->getTextWidth($pageText, $fontRegular, 9);
            $canvas->text($width - 28 - $pageTextWidth, $height - 22, $pageText, $fontRegular, 9, [0.35, 0.40, 0.50]);

            // Header from second page onward.
            if ($pageNumber > 1) {
                $headerTextWidth = $fontMetrics->getTextWidth($headerText, $fontBold, 9);
                $canvas->text($width - 28 - $headerTextWidth, 22, $headerText, $fontBold, 9, [0.10, 0.17, 0.28]);
            }
        });

        $filename = sprintf(
            'report-%s-%s.pdf',
            $type,
            now()->format('Ymd-His')
        );

        return response($dompdf->output(), 200, [
            'Content-Type' => 'application/pdf',
            'Content-Disposition' => 'attachment; filename="' . $filename . '"',
        ]);
    }

    private function buildReportData(Request $request): array
    {
        [$from, $to] = $this->resolveDateRange((string) $request->get('range', ''));
        $attivitaId = session()->get('attivita_attuale');
        $selectedCucinaId = (int) $request->get('cucina_id', 0);
        $selectedPagamento = (string) $request->get('pagamento', 'all');

        $cucine = Cucina::query()
            ->when($attivitaId, function ($query) use ($attivitaId) {
                $query->where('attivita_id', $attivitaId);
            })
            ->orderBy('nome')
            ->get(['id', 'nome']);

        $pagamentiCollection = Pagamento::query()
            ->with(['metodo_pagamento', 'ordine.righe_ordine.piatto'])
            ->when($attivitaId, function ($query) use ($attivitaId) {
                $query->where('attivita_id', $attivitaId);
            })
            ->when($selectedPagamento !== '' && $selectedPagamento !== 'all', function ($query) use ($selectedPagamento) {
                $query->whereHas('metodo_pagamento', function ($q) use ($selectedPagamento) {
                    $q->where('tipo', $selectedPagamento);
                });
            })
            ->when($selectedCucinaId > 0, function ($query) use ($selectedCucinaId) {
                $query->whereHas('ordine.righe_ordine.piatto', function ($q) use ($selectedCucinaId) {
                    $q->where('cucina_id', $selectedCucinaId);
                });
            })
            ->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()])
            ->orderByDesc('created_at')
            ->get();

        $movimentiCollection = PrimaNota::query()
            ->with(['pagamento.metodo_pagamento', 'ordine.righe_ordine.piatto'])
            ->when($attivitaId, function ($query) use ($attivitaId) {
                $query->where('attivita_id', $attivitaId);
            })
            ->when($selectedPagamento !== '' && $selectedPagamento !== 'all', function ($query) use ($selectedPagamento) {
                $query->whereHas('pagamento.metodo_pagamento', function ($q) use ($selectedPagamento) {
                    $q->where('tipo', $selectedPagamento);
                });
            })
            ->when($selectedCucinaId > 0, function ($query) use ($selectedCucinaId) {
                $query->whereHas('ordine.righe_ordine.piatto', function ($q) use ($selectedCucinaId) {
                    $q->where('cucina_id', $selectedCucinaId);
                });
            })
            ->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()])
            ->orderByDesc('created_at')
            ->get();

        if ($movimentiCollection->isNotEmpty()) {
            $movimenti = $movimentiCollection->map(function ($movimento) {
                $tipoRaw = strtolower((string) $movimento->tipo_movimento);
                $isUscita = $tipoRaw === 'uscita' || ((float) $movimento->importo) < 0;
                $tipo = $isUscita ? 'uscita' : 'entrata';

                $metodoPagamento = $movimento->pagamento?->metodo_pagamento;
                $metodo = $metodoPagamento?->nome ?? $metodoPagamento?->tipo ?? 'N/D';

                $categoria = $movimento->riferimento;
                if (empty($categoria)) {
                    if (!empty($movimento->ordine_id)) {
                        $categoria = 'Ordine';
                    } elseif (!empty($movimento->prenotazione_id)) {
                        $categoria = 'Prenotazione';
                    } else {
                        $categoria = 'Movimento';
                    }
                }

                $causaleBase = $movimento->causale;
                if (empty($causaleBase)) {
                    $causaleBase = !empty($movimento->pagamento_id)
                        ? 'Pagamento #' . $movimento->pagamento_id
                        : 'Movimento #' . $movimento->id;
                }

                $riferimenti = $this->resolveMovimentoRiferimenti($movimento);
                $causale = $riferimenti !== ''
                    ? $riferimenti . ' - ' . $causaleBase
                    : $causaleBase;

                return [
                    'timestamp' => optional($movimento->created_at)->format('Y-m-d H:i:s'),
                    'tipo' => $tipo,
                    'importo' => abs((float) $movimento->importo),
                    'categoria' => $categoria,
                    'causale' => $causale,
                    'metodo' => $metodo,
                ];
            })->values();
        } else {
            $movimenti = $pagamentiCollection->map(function ($pagamento) {
                $metodoPagamento = $pagamento->metodo_pagamento;
                $metodo = $metodoPagamento?->nome ?? $metodoPagamento?->tipo ?? 'N/D';

                return [
                    'timestamp' => optional($pagamento->created_at)->format('Y-m-d H:i:s'),
                    'tipo' => 'entrata',
                    'importo' => abs((float) $pagamento->importo),
                    'categoria' => 'Pagamento',
                    'causale' => $pagamento->causale ?: ('Pagamento #' . $pagamento->id),
                    'metodo' => $metodo,
                ];
            })->values();
        }

        $totEntrate = $movimenti->where('tipo', 'entrata')->sum('importo');
        $totUscite = $movimenti->where('tipo', 'uscita')->sum('importo');
        $saldo = $totEntrate - $totUscite;

        $attivitaNome = Attivita::find($attivitaId)?->nome ?? 'Attivita';
        $periodoLabel = $from->format('d/m/Y') . ' - ' . $to->format('d/m/Y');

        $incassoPerGiornoMap = $pagamentiCollection
            ->groupBy(function ($pagamento) {
                return optional($pagamento->created_at)->format('Y-m-d');
            })
            ->map(function ($items) {
                return (float) $items->sum(function ($pagamento) {
                    return (float) ($pagamento->importo ?? 0);
                });
            });

        $incassoChartLabels = [];
        $incassoChartSeries = [];
        foreach (CarbonPeriod::create($from->copy()->startOfDay(), $to->copy()->startOfDay()) as $date) {
            $dateKey = $date->format('Y-m-d');
            $incassoChartLabels[] = $date->format('d/m');
            $incassoChartSeries[] = (float) ($incassoPerGiornoMap[$dateKey] ?? 0);
        }

        $ordiniCollection = Ordine::query()
            ->when($attivitaId, function ($query) use ($attivitaId) {
                $query->where('attivita_id', $attivitaId);
            })
            ->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()])
            ->when($selectedPagamento !== '' && $selectedPagamento !== 'all', function ($query) use ($selectedPagamento) {
                $query->whereHas('pagamento.metodo_pagamento', function ($metodoQuery) use ($selectedPagamento) {
                    $metodoQuery->where('tipo', $selectedPagamento);
                });
            })
            ->when($selectedCucinaId > 0, function ($query) use ($selectedCucinaId) {
                $query->whereHas('righe_ordine.piatto', function ($q) use ($selectedCucinaId) {
                    $q->where('cucina_id', $selectedCucinaId);
                });
            })
            ->get(['id', 'created_at']);

        $dayLabels = [1 => 'Lun', 2 => 'Mar', 3 => 'Mer', 4 => 'Gio', 5 => 'Ven', 6 => 'Sab', 7 => 'Dom'];
        $dayAxis = array_values($dayLabels);
        $hourLabels = collect(range(0, 23))->map(function ($hour) {
            return str_pad((string) $hour, 2, '0', STR_PAD_LEFT) . ':00';
        })->values()->all();

        $heatmapMatrix = [];
        foreach ($hourLabels as $hourLabel) {
            $heatmapMatrix[$hourLabel] = array_fill(0, count($dayAxis), 0);
        }

        foreach ($ordiniCollection as $ordine) {
            if (!$ordine->created_at) {
                continue;
            }

            $date = Carbon::parse($ordine->created_at);
            $dayIndex = $date->dayOfWeekIso - 1; // Lun=0 ... Dom=6
            $hourLabel = str_pad((string) $date->hour, 2, '0', STR_PAD_LEFT) . ':00';

            if (!isset($heatmapMatrix[$hourLabel], $heatmapMatrix[$hourLabel][$dayIndex])) {
                continue;
            }

            $heatmapMatrix[$hourLabel][$dayIndex]++;
        }

        $heatmapSeries = collect($hourLabels)->map(function ($hourLabel) use ($heatmapMatrix) {
            return [
                'name' => $hourLabel,
                'data' => $heatmapMatrix[$hourLabel] ?? array_fill(0, 7, 0),
            ];
        })->values();

        $righeCucinaCollection = RigaOrdine::query()
            ->with(['piatto.cucina', 'ordine'])
            ->whereHas('ordine', function ($query) use ($attivitaId, $from, $to, $selectedPagamento) {
                $query
                    ->when($attivitaId, function ($q) use ($attivitaId) {
                        $q->where('attivita_id', $attivitaId);
                    })
                    ->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()])
                    ->when($selectedPagamento !== '' && $selectedPagamento !== 'all', function ($q) use ($selectedPagamento) {
                        $q->whereHas('pagamento.metodo_pagamento', function ($metodoQuery) use ($selectedPagamento) {
                            $metodoQuery->where('tipo', $selectedPagamento);
                        });
                    });
            })
            ->when($selectedCucinaId > 0, function ($query) use ($selectedCucinaId) {
                $query->whereHas('piatto', function ($q) use ($selectedCucinaId) {
                    $q->where('cucina_id', $selectedCucinaId);
                });
            })
            ->get();

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

                return [
                    'cucina' => $cucinaNome,
                    'ordini' => $items->pluck('ordine_id')->filter()->unique()->count(),
                    'righe' => $items->count(),
                    'quantita' => (int) $items->sum(function ($riga) {
                        return (int) ($riga->quantita ?? 0);
                    }),
                    'incasso' => (float) $items->sum(function ($riga) {
                        $qta = (float) ($riga->quantita ?? 0);
                        $prezzo = (float) ($riga->prezzo ?? 0);
                        return $qta * $prezzo;
                    }),
                ];
            })
            ->sortByDesc('incasso')
            ->values();

        $topPiattiRows = $righeCucinaCollection
            ->groupBy(function ($riga) {
                return $riga->piatto_id ?: 0;
            })
            ->map(function ($items) {
                $first = $items->first();
                return [
                    'piatto' => $first?->piatto?->nome ?? 'Piatto',
                    'quantita' => (int) $items->sum(function ($riga) {
                        return (int) ($riga->quantita ?? 0);
                    }),
                ];
            })
            ->sortByDesc('quantita')
            ->take(8)
            ->values();

        $paymentBreakdownRows = $pagamentiCollection
            ->groupBy(function ($pagamento) {
                return $pagamento->metodo_pagamento?->nome
                    ?? $pagamento->metodo_pagamento?->tipo
                    ?? 'N/D';
            })
            ->map(function ($items, $metodo) use ($totEntrate) {
                $importo = (float) $items->sum(function ($pagamento) {
                    return (float) ($pagamento->importo ?? 0);
                });
                return [
                    'metodo' => (string) $metodo,
                    'count' => $items->count(),
                    'importo' => $importo,
                    'percent' => $totEntrate > 0 ? round(($importo / $totEntrate) * 100, 1) : 0,
                ];
            })
            ->sortByDesc('importo')
            ->values();

        $ordiniCount = $ordiniCollection->count();
        $ticketMedio = $ordiniCount > 0 ? ((float) $totEntrate / $ordiniCount) : 0;
        $coperti = (int) $righeCucinaCollection->sum(function ($riga) {
            return (int) ($riga->quantita ?? 0);
        });

        return [
            'movimenti' => $movimenti,
            'totEntrate' => $totEntrate,
            'totUscite' => $totUscite,
            'saldo' => $saldo,
            'attivitaNome' => $attivitaNome,
            'periodoLabel' => $periodoLabel,
            'rangeDateFrom' => $from->format('Y-m-d'),
            'rangeDateTo' => $to->format('Y-m-d'),
            'cucine' => $cucine,
            'cucineReportRows' => $cucineReportRows,
            'selectedCucinaId' => $selectedCucinaId,
            'selectedPagamento' => $selectedPagamento,
            'kpi' => [
                'ordini' => $ordiniCount,
                'incasso' => (float) $totEntrate,
                'ticket_medio' => (float) $ticketMedio,
                'coperti' => $coperti,
            ],
            'incassoChartLabels' => $incassoChartLabels,
            'incassoChartSeries' => $incassoChartSeries,
            'heatmapDayLabels' => $dayAxis,
            'heatmapSeries' => $heatmapSeries,
            'topPiattiLabels' => $topPiattiRows->pluck('piatto')->values(),
            'topPiattiSeries' => $topPiattiRows->pluck('quantita')->values(),
            'paymentBreakdownRows' => $paymentBreakdownRows,
        ];
    }

    private function resolveDateRange(string $range): array
    {
        $start = now()->startOfMonth();
        $end = now()->endOfDay();

        if (!empty($range) && str_contains($range, ' to ')) {
            [$fromRaw, $toRaw] = array_map('trim', explode(' to ', $range, 2));
            try {
                $start = Carbon::parse($fromRaw)->startOfDay();
                $end = Carbon::parse($toRaw)->endOfDay();
            } catch (\Throwable $e) {
                // Keep fallback defaults on parse errors.
            }
        }

        return [$start, $end];
    }

    private function resolveReportPdfTitle(string $type): string
    {
        return match ($type) {
            'incassi' => 'Report Incassi',
            'cucine' => 'Report Cucine',
            default => 'Movimenti in EUR',
        };
    }

    private function resolveMovimentoRiferimenti(PrimaNota $movimento): string
    {
        $riferimenti = [];

        if (!empty($movimento->ordine_id)) {
            $riferimenti[] = 'Ordine #' . $movimento->ordine_id;
        }
        if (!empty($movimento->pagamento_id)) {
            $riferimenti[] = 'Pagamento #' . $movimento->pagamento_id;
        }
        if (!empty($movimento->fornitore_id)) {
            $fornitoreNome = trim((string) ($movimento->fornitore?->nome ?? ''));
            $riferimenti[] = $fornitoreNome !== ''
                ? 'Fornitore: ' . $fornitoreNome
                : 'Fornitore #' . $movimento->fornitore_id;
        }
        if (!empty($movimento->prenotazione_id)) {
            $riferimenti[] = 'Prenotazione #' . $movimento->prenotazione_id;
        }
        if (!empty($movimento->evento_id)) {
            $riferimenti[] = 'Evento #' . $movimento->evento_id;
        }

        return implode(' · ', $riferimenti);
    }
}
