<?php

namespace App\Http\Controllers;

use App\Models\Attivita;
use App\Models\PrimaNota;
use App\Models\Pagamento;
use App\Models\RigaOrdine;
use App\Services\Bilancio\BilancioServizioService;
use Illuminate\Http\Request;
use Illuminate\Routing\Controllers\Middleware;
use Carbon\Carbon;
use Carbon\CarbonPeriod;

class BilancioController extends Controller
{
    public static $permission_group = "Bilancio";
    public static $permissions = [
        'view-bilancio-oggi' => 'Vedi oggi',
        'view-bilancio' => 'Vedi',
        'create-bilancio' => 'Crea',
        'edit-bilancio' => 'Modifica',
        'delete-bilancio' => 'Elimina',
    ];

    public static function middleware(): array
    {
        return [
            new Middleware('permission:view-bilancio-oggi', only: ['oggi']),
            new Middleware('permission:view-bilancio', only: ['index']),
            new Middleware('permission:create-bilancio|edit-bilancio|delete-bilancio', only: ['store', 'update', 'destroy']),
        ];
    }

    public function oggi(Request $request, BilancioServizioService $bilancioServizio)
    {
        $attivitaId = (int) session()->get('attivita_attuale');
        $attivita = Attivita::find($attivitaId);
        $period = $bilancioServizio->currentPeriod($attivitaId);
        $periodStart = $period['start'];
        $periodEnd = $period['end'];
        $ultimaChiusuraAt = $period['ultimaChiusuraAt'];

        $report = $bilancioServizio->forPeriod($attivitaId, $periodStart, $periodEnd);

        return view('bilancio.oggi', [
            'attivita' => $attivita,
            'periodoStart' => $periodStart,
            'periodoEnd' => $periodEnd,
            'ultimaChiusuraAt' => $ultimaChiusuraAt,
            'periodoLabel' => $bilancioServizio->periodoLabel($periodStart, $periodEnd, $ultimaChiusuraAt),
            'cucineRows' => $report['cucineRows'],
            'paymentSummary' => $report['paymentSummary'],
            'totaleIncassoOggi' => $report['totaleIncasso'],
            'totaleTransazioniOggi' => $report['totaleTransazioni'],
            'totPiattiOrdinati' => $report['totPiattiOrdinati'],
            'heatmapHourLabels' => $report['heatmapHourLabels'],
            'heatmapSeries' => $report['heatmapSeries'],
        ]);
    }

    public function index(Request $request)
    {
        $attivitaId = session()->get('attivita_attuale');
        $attivita = Attivita::find($attivitaId);

        $year = (int) $request->get('anno', now()->year);
        if ($year < 2000 || $year > 2100) {
            $year = (int) now()->year;
        }

        $yearStart = Carbon::create($year, 1, 1)->startOfDay();
        $yearEnd = Carbon::create($year, 12, 31)->endOfDay();

        $movimentiCollection = PrimaNota::query()
            ->with(['fornitore', 'pagamento.metodo_pagamento', 'categoria_contabile'])
            ->when($attivitaId, function ($query) use ($attivitaId) {
                $query->where('attivita_id', $attivitaId);
            })
            ->whereBetween('created_at', [$yearStart, $yearEnd])
            ->orderBy('created_at')
            ->get();

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

            return [
                'data' => $movimento->created_at,
                'tipo' => $isUscita ? 'uscita' : 'entrata',
                'importo' => abs((float) ($movimento->importo ?? 0)),
                'pagamento_id' => (int) ($movimento->pagamento_id ?? 0),
                'fornitore_id' => (int) ($movimento->fornitore_id ?? 0),
                'fornitore_nome' => $movimento->fornitore?->nome,
                'causale' => trim((string) ($movimento->causale ?? '')),
                'riferimento' => trim((string) ($movimento->riferimento ?? '')),
                'categoria_id' => (int) ($movimento->categoria_contabile_id ?? 0),
                'categoria_nome' => trim((string) ($movimento->categoria_contabile?->nome ?? '')),
                'categoria_colore' => trim((string) ($movimento->categoria_contabile?->colore ?? '')),
                'metodo' => $movimento->pagamento?->metodo_pagamento?->nome
                    ?? $movimento->pagamento?->metodo_pagamento?->tipo
                    ?? null,
            ];
        })->values();

        $ricaviTotali = (float) $movimenti->where('tipo', 'entrata')->sum('importo');
        $costiTotali = (float) $movimenti->where('tipo', 'uscita')->sum('importo');

        // Fallback per ricavi se la prima nota non contiene entrate.
        $pagamentiCollection = Pagamento::query()
            ->with('metodo_pagamento')
            ->when($attivitaId, function ($query) use ($attivitaId) {
                $query->where('attivita_id', $attivitaId);
            })
            ->whereBetween('created_at', [$yearStart, $yearEnd])
            ->orderBy('created_at')
            ->get();

        if ($ricaviTotali <= 0 && $pagamentiCollection->isNotEmpty()) {
            $ricaviTotali = (float) $pagamentiCollection->sum(function ($pagamento) {
                return (float) ($pagamento->importo ?? 0);
            });
        }

        $saldo = $ricaviTotali - $costiTotali;

        $mesiBase = collect(CarbonPeriod::create($yearStart->copy(), '1 month', $yearEnd->copy()))
            ->map(function (Carbon $date) {
                return [
                    'month_key' => $date->format('Y-m'),
                    'mese' => $date->translatedFormat('M'),
                    'ricavi' => 0.0,
                    'costi' => 0.0,
                    'saldo' => 0.0,
                ];
            })
            ->keyBy('month_key');

        foreach ($movimenti as $movimento) {
            if (!$movimento['data']) {
                continue;
            }
            $monthKey = Carbon::parse($movimento['data'])->format('Y-m');
            if (!$mesiBase->has($monthKey)) {
                continue;
            }
            $row = $mesiBase->get($monthKey);
            if ($movimento['tipo'] === 'entrata') {
                $row['ricavi'] += $movimento['importo'];
            } else {
                $row['costi'] += $movimento['importo'];
            }
            $row['saldo'] = $row['ricavi'] - $row['costi'];
            $mesiBase->put($monthKey, $row);
        }

        $andamentoMensile = $mesiBase->values();

        $costiRows = $movimenti
            ->where('tipo', 'uscita')
            ->groupBy(function ($movimento) {
                $categoriaId = (int) ($movimento['categoria_id'] ?? 0);
                $categoriaNome = trim((string) ($movimento['categoria_nome'] ?? ''));
                if ($categoriaId > 0 && $categoriaNome !== '') {
                    return 'cat:' . $categoriaId;
                }
                return 'cat:0';
            })
            ->map(function ($items, $groupKey) {
                $first = $items->first();
                $categoriaId = (int) ($first['categoria_id'] ?? 0);
                $categoriaNome = trim((string) ($first['categoria_nome'] ?? ''));
                $categoriaColore = trim((string) ($first['categoria_colore'] ?? ''));

                return [
                    'categoria_id' => $categoriaId,
                    'voce' => $categoriaNome !== '' ? $categoriaNome : 'Senza categoria',
                    'colore' => $categoriaColore,
                    'movimenti' => $items->count(),
                    'totale' => (float) $items->sum('importo'),
                ];
            })
            ->sortByDesc('totale')
            ->values();

        $ricaviBase = $movimenti->where('tipo', 'entrata');
        $ricaviRows = $ricaviBase
            ->groupBy(function ($movimento) {
                $categoriaId = (int) ($movimento['categoria_id'] ?? 0);
                $categoriaNome = trim((string) ($movimento['categoria_nome'] ?? ''));
                if ($categoriaId > 0 && $categoriaNome !== '') {
                    return 'cat:' . $categoriaId;
                }
                return 'cat:0';
            })
            ->map(function ($items, $groupKey) {
                $first = $items->first();
                $categoriaId = (int) ($first['categoria_id'] ?? 0);
                $categoriaNome = trim((string) ($first['categoria_nome'] ?? ''));
                $categoriaColore = trim((string) ($first['categoria_colore'] ?? ''));

                return [
                    'categoria_id' => $categoriaId,
                    'voce' => $categoriaNome !== '' ? $categoriaNome : 'Senza categoria',
                    'colore' => $categoriaColore,
                    'movimenti' => $items->count(),
                    'totale' => (float) $items->sum('importo'),
                ];
            })
            ->sortByDesc('totale')
            ->values();

        // Fallback: se Prima Nota non contiene entrate, usa i pagamenti dell'anno.
        if ($ricaviRows->isEmpty() && $pagamentiCollection->isNotEmpty()) {
            $ricaviRows = collect([
                [
                    'categoria_id' => 0,
                    'voce' => 'Senza categoria',
                    'colore' => '',
                    'movimenti' => $pagamentiCollection->count(),
                    'totale' => (float) $pagamentiCollection->sum(function ($pagamento) {
                        return (float) ($pagamento->importo ?? 0);
                    }),
                ],
            ]);
        }

        $pagamentiCountAnno = (int) $pagamentiCollection->count();
        $ticketMedioPagamenti = $pagamentiCountAnno > 0
            ? ((float) $pagamentiCollection->sum(function ($p) {
                return (float) ($p->importo ?? 0);
            }) / $pagamentiCountAnno)
            : 0.0;

        $categoriaPerformance = $movimenti
            ->groupBy(function ($movimento) {
                $categoriaId = (int) ($movimento['categoria_id'] ?? 0);
                $categoriaNome = trim((string) ($movimento['categoria_nome'] ?? ''));
                if ($categoriaId > 0 && $categoriaNome !== '') {
                    return 'cat:' . $categoriaId;
                }
                return 'cat:0';
            })
            ->map(function ($items) {
                $first = $items->first();
                $ricavi = (float) $items->where('tipo', 'entrata')->sum('importo');
                $costi = (float) $items->where('tipo', 'uscita')->sum('importo');
                return [
                    'categoria' => trim((string) ($first['categoria_nome'] ?? '')) ?: 'Senza categoria',
                    'ricavi' => $ricavi,
                    'costi' => $costi,
                    'saldo' => $ricavi - $costi,
                    'movimenti' => $items->count(),
                ];
            })
            ->sortByDesc(function ($r) {
                return abs((float) ($r['saldo'] ?? 0));
            })
            ->values();

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

        $piattiPerformance = $righeOrdineAnnuali
            ->groupBy('piatto_id')
            ->map(function ($items) {
                $first = $items->first();
                $quantita = (int) $items->sum(function ($r) {
                    return (int) ($r->quantita ?? 0);
                });
                $totale = (float) $items->sum(function ($r) {
                    return ((float) ($r->quantita ?? 0)) * ((float) ($r->prezzo ?? 0));
                });
                return [
                    'nome' => $first?->piatto?->nome ?? 'Piatto',
                    'quantita' => $quantita,
                    'totale' => $totale,
                ];
            })
            ->sortByDesc('totale')
            ->values();

        $cucinePerformance = $righeOrdineAnnuali
            ->groupBy(function ($riga) {
                return (int) ($riga->piatto?->cucina_id ?? 0);
            })
            ->map(function ($items) {
                $first = $items->first();
                $quantita = (int) $items->sum(function ($r) {
                    return (int) ($r->quantita ?? 0);
                });
                $totale = (float) $items->sum(function ($r) {
                    return ((float) ($r->quantita ?? 0)) * ((float) ($r->prezzo ?? 0));
                });
                return [
                    'nome' => $first?->piatto?->cucina?->nome ?? 'Cucina N/D',
                    'quantita' => $quantita,
                    'totale' => $totale,
                ];
            })
            ->sortByDesc('totale')
            ->values();

        $pagamentiPerformance = $pagamentiCollection
            ->groupBy(function ($pagamento) {
                return $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(function ($p) {
                        return (float) ($p->importo ?? 0);
                    }),
                ];
            })
            ->sortByDesc('totale')
            ->values();

        $ultimiTreMesi = $andamentoMensile->take(-3);
        $previsioneProssimoMese = [
            'ricavi' => (float) $ultimiTreMesi->avg('ricavi'),
            'costi' => (float) $ultimiTreMesi->avg('costi'),
            'saldo' => (float) $ultimiTreMesi->avg('saldo'),
            'mesi_considerati' => $ultimiTreMesi->count(),
        ];

        $giorniIso = [
            1 => 'Lun',
            2 => 'Mar',
            3 => 'Mer',
            4 => 'Gio',
            5 => 'Ven',
            6 => 'Sab',
            7 => 'Dom',
        ];

        $oreLabels = collect(range(0, 23))->map(function ($h) {
            return str_pad((string) $h, 2, '0', STR_PAD_LEFT);
        })->values();

        $affluenzaHeatmapSeries = collect($giorniIso)->map(function ($giornoLabel, $giornoIso) use ($pagamentiCollection, $oreLabels) {
            $data = $oreLabels->map(function ($oraLabel) use ($pagamentiCollection, $giornoIso) {
                $ora = (int) $oraLabel;
                $count = (int) $pagamentiCollection->filter(function ($pagamento) use ($giornoIso, $ora) {
                    if (empty($pagamento->created_at)) {
                        return false;
                    }
                    return ((int) $pagamento->created_at->isoWeekday() === (int) $giornoIso)
                        && ((int) $pagamento->created_at->format('H') === $ora);
                })->count();

                return [
                    'x' => $oraLabel,
                    'y' => $count,
                ];
            })->values()->all();

            return [
                'name' => $giornoLabel,
                'data' => $data,
            ];
        })->values();

        $affluenzaGiorniSeries = collect($giorniIso)->map(function ($giornoLabel, $giornoIso) use ($pagamentiCollection) {
            return (int) $pagamentiCollection->filter(function ($pagamento) use ($giornoIso) {
                return !empty($pagamento->created_at) && ((int) $pagamento->created_at->isoWeekday() === (int) $giornoIso);
            })->count();
        })->values();

        $affluenzaGiorniLabels = collect(array_values($giorniIso))->values();

        return view('bilancio.index', [
            'attivita' => $attivita,
            'selectedYear' => $year,
            'anniSelezionabili' => collect(range((int) now()->year, (int) now()->year - 5)),
            'ricaviTotali' => $ricaviTotali,
            'costiTotali' => $costiTotali,
            'saldoTotale' => $saldo,
            'movimentiCount' => $movimenti->count(),
            'andamentoMensile' => $andamentoMensile,
            'costiRows' => $costiRows,
            'ricaviRows' => $ricaviRows,
            'categoriaPerformance' => $categoriaPerformance,
            'piattiPerformance' => $piattiPerformance,
            'cucinePerformance' => $cucinePerformance,
            'pagamentiPerformance' => $pagamentiPerformance,
            'previsioneProssimoMese' => $previsioneProssimoMese,
            'ticketMedioPagamenti' => $ticketMedioPagamenti,
            'pagamentiCountAnno' => $pagamentiCountAnno,
            'affluenzaHeatmapSeries' => $affluenzaHeatmapSeries,
            'affluenzaGiorniLabels' => $affluenzaGiorniLabels,
            'affluenzaGiorniSeries' => $affluenzaGiorniSeries,
        ]);
    }
}
