<?php

namespace App\Livewire\Studio\Dashboard;

use App\Models\Bottle;
use App\Models\Brand;
use App\Models\Category;
use App\Models\Customer;
use App\Models\Expense;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Outlet;
use App\Models\Payment;
use App\Models\Payroll;
use App\Models\Perfume;
use App\Models\Product;
use App\Models\Purchase;
use App\Models\Tier;
use App\Models\User;
use App\Models\Voucher;
use App\Traits\Notification\WithSubscribeNotification;
use Carbon\Carbon;
use Illuminate\Contracts\View\View;
use Illuminate\Support\Facades\DB;
use Livewire\Attributes\Title;
use Livewire\Component;

#[Title('Analisa')]
class Analysis extends Component
{
    use WithSubscribeNotification;

    public string $period = 'all';

    public array $outlets = [];

    public array $range = [
        'start' => null,
        'end' => null,
    ];

    public array $selectedOutletIds = [];

    public array $revenueTrendChart = [];

    public array $hourlySalesChart = [];

    public array $categoryDistributionChart = [];

    public array $paymentMethodChart = [];

    public array $outletPerformanceChart = [];

    public array $memberGrowthChart = [];

    public array $dayOfWeekSalesChart = [];

    public array $customerTypeChart = [];

    public array $transactionTrendChart = [];

    public array $aovTrendChart = [];

    public array $expenseBreakdownChart = [];

    public array $revenueVsCostChart = [];

    public array $customerRetentionChart = [];

    public array $voucherUsageTrendChart = [];

    public function mount(): void
    {
        $this->outlets = auth()->user()->outlets()->get()->pluck('name', 'id')->toArray();

        $this->selectedOutletIds = [];
    }

    public function render(): View
    {
        $outletIds = $this->selectedOutletIds;
        $period = $this->period;
        $range = $this->range;

        $dateQuery = match ($period) {
            '7' => now()->subDays(1),
            '30' => now()->subDays(30),
            '90' => now()->subDays(90),
            '365' => now()->subDays(365),
            default => null,
        };

        $startDate = ! empty($range['start']) ? Carbon::parse($range['start'])->startOfDay() : null;
        $endDate = ! empty($range['end']) ? Carbon::parse($range['end'])->endOfDay() : null;

        if ($startDate && $endDate) {
            $dateQuery = null;
        }

        $filterDate = function ($q) use ($dateQuery, $startDate, $endDate) {
            if ($startDate && $endDate) {
                $q->whereBetween('created_at', [$startDate, $endDate]);
            } elseif ($dateQuery) {
                $q->where('created_at', '>=', $dateQuery);
            }
        };

        $countingData = [
            [
                'title' => 'Outlet',
                'value' => Outlet::when($dateQuery || ($startDate && $endDate), $filterDate)->count(),
            ],
            [
                'title' => 'Pegawai',
                'value' => User::whereHas('employee')
                    ->when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate)
                    ->withoutDeveloper()
                    ->count(),
            ],
            [
                'title' => 'Tier',
                'value' => Tier::when($dateQuery || ($startDate && $endDate), $filterDate)->count(),
            ],
            [
                'title' => 'Voucher Aktif',
                'value' => Voucher::when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate)
                    ->active()
                    ->count(),
            ],
            [
                'title' => 'Member',
                'value' => User::whereHas('customer')
                    ->when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate)
                    ->count(),
            ],
            [
                'title' => 'Customer',
                'value' => Customer::whereDoesntHave('user')
                    ->when($dateQuery || ($startDate && $endDate), $filterDate)
                    ->count(),
            ],
            [
                'title' => 'Kategori',
                'value' => Category::when($dateQuery || ($startDate && $endDate), $filterDate)->count(),
            ],
            [
                'title' => 'Merek',
                'value' => Brand::when($dateQuery || ($startDate && $endDate), $filterDate)->count(),
            ],
            [
                'title' => 'Parfum',
                'value' => Perfume::when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate)
                    ->count(),
            ],
            [
                'title' => 'Produk',
                'value' => Product::when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate)
                    ->count(),
            ],
            [
                'title' => 'Botol',
                'value' => Bottle::when(! empty($outletIds), fn ($q) => $q->whereHas('outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate)
                    ->count(),
            ],
        ];

        $orderQuery = Order::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
            ->when($dateQuery || ($startDate && $endDate), $filterDate);

        $totalIncome = (clone $orderQuery)->sum('total');
        $totalCogs = (clone $orderQuery)->sum('cogs');
        $totalDiscount = (clone $orderQuery)->sum('discount');

        $expenseQuery = Expense::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
            ->when($dateQuery || ($startDate && $endDate), $filterDate);
        $totalExpense = $expenseQuery->sum('amount');

        $payrollQuery = Payroll::when(! empty($outletIds), fn ($q) => $q->whereHas('user.outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))
            ->when($dateQuery || ($startDate && $endDate), $filterDate)
            ->paid();
        $totalPayroll = $payrollQuery->sum('total_salary');

        $grossProfit = $totalIncome - $totalCogs - $totalDiscount;
        $netProfit = $grossProfit - $totalExpense - $totalPayroll;

        $totalOrdersCount = (clone $orderQuery)->count();
        $totalItemsCount = OrderItem::whereHas('order', fn ($q) => $q->when(! empty($outletIds), fn ($q2) => $q2->whereIn('outlet_id', $outletIds))->when($dateQuery || ($startDate && $endDate), $filterDate))->sum('quantity');

        $aov = $totalOrdersCount > 0 ? $totalIncome / $totalOrdersCount : 0;
        $profitMargin = $totalIncome > 0 ? ($netProfit / $totalIncome) * 100 : 0;
        $itemsPerOrder = $totalOrdersCount > 0 ? $totalItemsCount / $totalOrdersCount : 0;

        $customerOrders = (clone $orderQuery)->select('customer_id', DB::raw('count(*) as count'))
            ->whereNotNull('customer_id')
            ->groupBy('customer_id')
            ->get();
        $repeatCustomers = $customerOrders->filter(fn ($c) => $c->count > 1)->count();
        $totalCustomers = $customerOrders->count();
        $loyaltyRate = $totalCustomers > 0 ? ($repeatCustomers / $totalCustomers) * 100 : 0;

        $orders = array_filter([
            [
                'title' => 'Average Order Value (AOV)',
                'value' => formatCurrencyNumber($aov, 'Rp'),
            ],
            auth()->user()->hasRole(['Developer', 'Owner']) ? [
                'title' => 'Profit Margin',
                'value' => round($profitMargin, 1).'%',
            ] : null,
            [
                'title' => 'Loyalty Rate',
                'value' => round($loyaltyRate, 1).'%',
            ],
            [
                'title' => 'Item per Transaksi',
                'value' => round($itemsPerOrder, 1),
            ],
            [
                'title' => 'Total Order',
                'value' => formatCurrencyNumber($totalOrdersCount),
            ],
            [
                'title' => 'Pendapatan',
                'value' => formatCurrencyNumber($totalIncome, 'Rp'),
            ],

            auth()->user()->hasRole(['Developer', 'Owner']) ? [
                'title' => 'HPP',
                'value' => formatCurrencyNumber($totalCogs, 'Rp'),
            ] : null,

            auth()->user()->hasRole(['Developer', 'Owner']) ? [
                'title' => 'Laba Kotor',
                'value' => formatCurrencyNumber($grossProfit, 'Rp'),
            ] : null,

            auth()->user()->hasRole(['Developer', 'Owner']) ? [
                'title' => 'Laba Bersih',
                'value' => formatCurrencyNumber($netProfit, 'Rp'),
            ] : null,

            [
                'title' => 'Diskon',
                'value' => formatCurrencyNumber($totalDiscount, 'Rp'),
            ],
            [
                'title' => 'Voucher Terpakai',
                'value' => formatCurrencyNumber(
                    (clone $orderQuery)->whereNotNull('voucher_id')->count()
                ),
            ],
            [
                'title' => 'Parfum Terjual',
                'value' => formatCurrencyNumber(
                    OrderItem::where('orderable_type', Perfume::class)
                        ->whereHas('order', fn ($q) => $q->when(! empty($outletIds), fn ($q2) => $q2->whereIn('outlet_id', $outletIds))->when($dateQuery || ($startDate && $endDate), $filterDate))
                        ->sum('quantity')
                ).' ml',
            ],
            [
                'title' => 'Produk Terjual',
                'value' => formatCurrencyNumber(
                    OrderItem::where('orderable_type', Product::class)
                        ->whereHas('order', fn ($q) => $q->when(! empty($outletIds), fn ($q2) => $q2->whereIn('outlet_id', $outletIds))->when($dateQuery || ($startDate && $endDate), $filterDate))
                        ->sum('quantity')
                ).' pcs',
            ],
            [
                'title' => 'Botol Terjual',
                'value' => formatCurrencyNumber(
                    OrderItem::where('orderable_type', Bottle::class)
                        ->whereHas('order', fn ($q) => $q->when(! empty($outletIds), fn ($q2) => $q2->whereIn('outlet_id', $outletIds))->when($dateQuery || ($startDate && $endDate), $filterDate))
                        ->sum('quantity')
                ).' pcs',
            ],
        ]);

        $expenses = array_filter([
            [
                'title' => 'Beban Toko',
                'value' => formatCurrencyNumber(
                    Expense::when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
                        ->when($dateQuery || ($startDate && $endDate), $filterDate)
                        ->sum('amount'),
                    'Rp'
                ),
            ],

            auth()->user()->hasRole(['Developer', 'Owner']) ? [
                'title' => 'Belanja Barang',
                'value' => formatCurrencyNumber(
                    Purchase::when($dateQuery || ($startDate && $endDate), $filterDate)
                        ->sum('total'),
                    'Rp'
                ),
            ] : null,

            [
                'title' => 'Penggajian',
                'value' => formatCurrencyNumber(
                    Payroll::when(! empty($outletIds), fn ($q) => $q->whereHas('user.outlets', fn ($q2) => $q2->whereIn('outlets.id', $outletIds)))
                        ->when($dateQuery || ($startDate && $endDate), $filterDate)
                        ->paid()
                        ->sum('total_salary'),
                    'Rp'
                ),
            ],
        ]);

        $topPerfumes = OrderItem::where('orderable_type', Perfume::class)
            ->whereHas('order', function ($q) use ($outletIds, $dateQuery, $startDate, $endDate, $filterDate) {
                $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate);
            })
            ->with('orderable')
            ->select('orderable_id', 'orderable_type', DB::raw('SUM(quantity) as total_sold'), DB::raw('SUM(unit_price * quantity) as total_sales_value'))
            ->groupBy('orderable_id', 'orderable_type')
            ->orderByDesc('total_sales_value')
            ->take(5)
            ->get()
            ->map(fn ($item) => [
                'name' => $item->orderable?->name ?? 'Unknown',
                'sale_price' => formatCurrencyNumber($item->orderable?->sale_price ?? 0, 'Rp'),
                'total_sold' => formatCurrencyNumber($item->total_sold),
                'total_sales' => formatCurrencyNumber($item->total_sales_value, 'Rp'),
            ])
            ->toArray();

        $topProducts = OrderItem::where('orderable_type', Product::class)
            ->whereHas('order', function ($q) use ($outletIds, $dateQuery, $startDate, $endDate, $filterDate) {
                $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate);
            })
            ->with('orderable')
            ->select('orderable_id', 'orderable_type', DB::raw('SUM(quantity) as total_sold'), DB::raw('SUM(unit_price * quantity) as total_sales_value'))
            ->groupBy('orderable_id', 'orderable_type')
            ->orderByDesc('total_sales_value')
            ->take(5)
            ->get()
            ->map(fn ($item) => [
                'name' => $item->orderable?->name ?? 'Unknown',
                'sale_price' => formatCurrencyNumber($item->orderable?->sale_price ?? 0, 'Rp'),
                'total_sold' => formatCurrencyNumber($item->total_sold),
                'total_sales' => formatCurrencyNumber($item->total_sales_value, 'Rp'),
            ])
            ->toArray();

        $topBottles = OrderItem::where('orderable_type', Bottle::class)
            ->whereHas('order', function ($q) use ($outletIds, $dateQuery, $startDate, $endDate, $filterDate) {
                $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate);
            })
            ->with('orderable')
            ->select('orderable_id', 'orderable_type', DB::raw('SUM(quantity) as total_sold'), DB::raw('SUM(unit_price * quantity) as total_sales_value'))
            ->groupBy('orderable_id', 'orderable_type')
            ->orderByDesc('total_sales_value')
            ->take(5)
            ->get()
            ->map(fn ($item) => [
                'name' => $item->orderable?->name ?? 'Unknown',
                'sale_price' => formatCurrencyNumber($item->orderable?->sale_price ?? 0, 'Rp'),
                'total_sold' => formatCurrencyNumber($item->total_sold),
                'total_sales' => formatCurrencyNumber($item->total_sales_value, 'Rp'),
            ])
            ->toArray();

        // --- Chart Data Logic ---

        // 1. Revenue & Profit Trend
        $trendDates = collect(range(29, 0))->map(fn ($i) => now()->subDays($i)->format('Y-m-d'));
        $ordersTrend = Order::query()
            ->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
            ->where('created_at', '>=', now()->subDays(30)->startOfDay())
            ->select(DB::raw('DATE(created_at) as date'), DB::raw('SUM(total) as revenue'), DB::raw('SUM(total - cogs - discount) as gross_profit'), DB::raw('count(*) as count'))
            ->groupBy('date')
            ->get()
            ->keyBy('date');

        $expensesTrend = Expense::query()
            ->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
            ->where('created_at', '>=', now()->subDays(30)->startOfDay())
            ->select(DB::raw('DATE(created_at) as date'), DB::raw('SUM(amount) as total_expense'))
            ->groupBy('date')
            ->get()
            ->keyBy('date');

        $this->revenueTrendChart = [
            'labels' => $trendDates->map(fn ($d) => Carbon::parse($d)->format('d M'))->toArray(),
            'revenue' => $trendDates->map(fn ($d) => (int) ($ordersTrend->get($d)?->revenue ?? 0))->toArray(),
            'profit' => $trendDates->map(fn ($d) => (int) (($ordersTrend->get($d)?->gross_profit ?? 0) - ($expensesTrend->get($d)?->total_expense ?? 0)))->toArray(),
        ];

        // 2. busiest hour
        $hourlySales = (clone $orderQuery)
            ->select(DB::raw('HOUR(created_at) as hour'), DB::raw('count(*) as count'))
            ->groupBy('hour')
            ->orderBy('hour')
            ->get()
            ->keyBy('hour');

        $this->hourlySalesChart = [
            'labels' => collect(range(0, 23))->map(fn ($h) => str_pad($h, 2, '0', STR_PAD_LEFT).':00')->toArray(),
            'data' => collect(range(0, 23))->map(fn ($h) => $hourlySales->get($h)?->count ?? 0)->toArray(),
        ];

        // 3. Product Category Distribution
        $perfumeSales = OrderItem::where('orderable_type', Perfume::class)
            ->whereHas('order', function ($q) use ($outletIds, $dateQuery, $startDate, $endDate, $filterDate) {
                $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate);
            })->sum(DB::raw('unit_price * quantity'));

        $productSales = OrderItem::where('orderable_type', Product::class)
            ->whereHas('order', function ($q) use ($outletIds, $dateQuery, $startDate, $endDate, $filterDate) {
                $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate);
            })->sum(DB::raw('unit_price * quantity'));

        $bottleSales = OrderItem::where('orderable_type', Bottle::class)
            ->whereHas('order', function ($q) use ($outletIds, $dateQuery, $startDate, $endDate, $filterDate) {
                $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
                    ->when($dateQuery || ($startDate && $endDate), $filterDate);
            })->sum(DB::raw('unit_price * quantity'));

        $this->categoryDistributionChart = [
            'labels' => ['Parfum', 'Produk', 'Botol'],
            'data' => [(int) $perfumeSales, (int) $productSales, (int) $bottleSales],
        ];

        // 4. Payment Method
        $paymentMethods = Payment::whereHas('order', function ($q) use ($outletIds, $dateQuery, $startDate, $endDate, $filterDate) {
            $q->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
                ->when($dateQuery || ($startDate && $endDate), $filterDate);
        })
            ->select('method', DB::raw('SUM(amount) as total'))
            ->groupBy('method')
            ->get();

        $this->paymentMethodChart = [
            'labels' => $paymentMethods->map(fn ($pm) => $pm->method?->label() ?? 'Unknown')->toArray(),
            'data' => $paymentMethods->pluck('total')->toArray(),
        ];

        // 5. Outlet Performance
        $outletPerformance = Order::query()
            ->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
            ->when($dateQuery || ($startDate && $endDate), $filterDate)
            ->select('outlet_id', DB::raw('SUM(total) as revenue'))
            ->groupBy('outlet_id')
            ->with('outlet:id,name')
            ->get();

        $this->outletPerformanceChart = [
            'labels' => $outletPerformance->map(fn ($op) => $op->outlet?->name ?? 'Outlet #'.$op->outlet_id)->toArray(),
            'data' => $outletPerformance->pluck('revenue')->toArray(),
        ];

        // 6. Member Growth (Area Chart) - Cumulative
        $memberGrowth = User::whereHas('customer')
            ->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as count'))
            ->groupBy('date')
            ->orderBy('date')
            ->get();

        $cumulativeCount = 0;
        $memberChartData = $memberGrowth->map(function ($item) use (&$cumulativeCount) {
            $cumulativeCount += $item->count;

            return [
                'date' => Carbon::parse($item->date)->format('d M Y'),
                'count' => $cumulativeCount,
            ];
        });

        $this->memberGrowthChart = [
            'labels' => $memberChartData->pluck('date')->toArray(),
            'data' => $memberChartData->pluck('count')->toArray(),
        ];

        // 7. Day of Week Sales
        $dayOfWeekSales = (clone $orderQuery)
            ->select(DB::raw('DAYOFWEEK(created_at) as day'), DB::raw('SUM(total) as revenue'))
            ->groupBy('day')
            ->get()
            ->keyBy('day');

        $days = [
            1 => 'Minggu',
            2 => 'Senin',
            3 => 'Selasa',
            4 => 'Rabu',
            5 => 'Kamis',
            6 => 'Jumat',
            7 => 'Sabtu',
        ];

        $this->dayOfWeekSalesChart = [
            'labels' => array_values($days),
            'data' => collect(range(1, 7))->map(fn ($d) => (int) ($dayOfWeekSales->get($d)?->revenue ?? 0))->toArray(),
        ];

        // 8. Customer Type (Member vs Guest)
        $memberOrders = (clone $orderQuery)->whereNotNull('customer_id')->count();
        $guestOrders = (clone $orderQuery)->whereNull('customer_id')->count();

        $this->customerTypeChart = [
            'labels' => ['Member', 'Guest (Umum)'],
            'data' => [$memberOrders, $guestOrders],
        ];

        // 9. Transaction Trend (Daily Count)
        $this->transactionTrendChart = [
            'labels' => $trendDates->map(fn ($d) => Carbon::parse($d)->format('d M'))->toArray(),
            'data' => $trendDates->map(fn ($d) => (int) ($ordersTrend->get($d)?->count ?? 0))->toArray(),
        ];

        // 10. AOV Trend (Daily Revenue / Daily Count)
        $this->aovTrendChart = [
            'labels' => $trendDates->map(fn ($d) => Carbon::parse($d)->format('d M'))->toArray(),
            'data' => $trendDates->map(function ($d) use ($ordersTrend) {
                $rev = $ordersTrend->get($d)?->revenue ?? 0;
                $count = $ordersTrend->get($d)?->count ?? 0;

                return $count > 0 ? (int) ($rev / $count) : 0;
            })->toArray(),
        ];

        // 11. Expense Breakdown (Operational vs Payroll)
        $this->expenseBreakdownChart = [
            'labels' => ['Beban Operasional', 'Gaji (Payroll)'],
            'data' => [(int) $totalExpense, (int) $totalPayroll],
        ];

        // 12. Revenue vs Total Cost (COGS + Expense + Payroll)
        $this->revenueVsCostChart = [
            'labels' => ['Pendapatan', 'Total Biaya (HPP + Beban + Gaji)'],
            'data' => [(int) $totalIncome, (int) ($totalCogs + $totalExpense + $totalPayroll)],
        ];

        // 13. Customer Retention (New vs Returning Transactions)
        $this->customerRetentionChart = [
            'labels' => ['Pelanggan Setia (Repeat)', 'Pelanggan Baru/Sekali'],
            'data' => [$repeatCustomers, max(0, $totalCustomers - $repeatCustomers)],
        ];

        // 14. Voucher Usage Trend
        $vouchersTrend = Order::query()
            ->when(! empty($outletIds), fn ($q) => $q->whereIn('outlet_id', $outletIds))
            ->where('created_at', '>=', now()->subDays(30)->startOfDay())
            ->whereNotNull('voucher_id')
            ->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as count'))
            ->groupBy('date')
            ->get()
            ->keyBy('date');

        $this->voucherUsageTrendChart = [
            'labels' => $trendDates->map(fn ($d) => Carbon::parse($d)->format('d M'))->toArray(),
            'data' => $trendDates->map(fn ($d) => (int) ($vouchersTrend->get($d)?->count ?? 0))->toArray(),
        ];

        return view('livewire.studio.dashboard.analysis', [
            'pageTitle' => 'Analisa',
            'countingData' => $countingData,
            'orders' => $orders,
            'expenses' => $expenses,
            'topPerfumes' => $topPerfumes,
            'topProducts' => $topProducts,
            'topBottles' => $topBottles,
        ]);
    }
}
