<?php

namespace App\Services;

use App\Enums\OrderStatus;
use App\Enums\PaymentMethod;
use App\Models\Order;
use App\Models\OrderItem;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;

/**
 * All the query logic behind the Dashboard's "at a glance" charts and the
 * Reports page's date-range deep dive lives here, shared, so the two
 * controllers don't each grow their own copy of "how do we compute sales."
 * Cancelled orders are excluded from every revenue/sales figure — they
 * were never realized income.
 */
class AnalyticsService
{
    /**
     * Categorical colors for the order-status doughnut — a fixed,
     * validated 6-hue order (see Step 14's palette check), chosen to
     * still read sensibly against the badge colors already used
     * everywhere else in the admin panel (green≈delivered, red≈cancelled)
     * while passing CVD adjacency checks as an ordered set.
     */
    public const ORDER_STATUS_COLORS = ['#2a78d6', '#eb6834', '#1baf7a', '#4a3aa7', '#008300', '#e34948'];

    /** First N slots of the validated reference categorical palette (fixed order, never reassigned per filter). */
    public const CATEGORICAL_COLORS = ['#2a78d6', '#eb6834', '#1baf7a', '#eda100', '#e87ba4', '#008300', '#4a3aa7', '#e34948'];

    /**
     * @return array{labels: array<string>, data: array<float>}
     */
    public function salesTrend(Carbon $from, Carbon $to): array
    {
        $rows = Order::where('order_status', '!=', OrderStatus::Cancelled->value)
            ->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()])
            ->selectRaw('DATE(created_at) as date, SUM(grand_total) as total')
            ->groupBy('date')
            ->pluck('total', 'date');

        $labels = [];
        $data = [];
        $cursor = $from->copy();

        while ($cursor->lte($to)) {
            $key = $cursor->format('Y-m-d');
            $labels[] = $cursor->format('M j');
            $data[] = (float) ($rows[$key] ?? 0);
            $cursor->addDay();
        }

        return compact('labels', 'data');
    }

    /**
     * @return array{labels: array<string>, data: array<int>, colors: array<string>}
     */
    public function orderStatusBreakdown(): array
    {
        $counts = Order::selectRaw('order_status, COUNT(*) as count')
            ->groupBy('order_status')
            ->pluck('count', 'order_status');

        $labels = [];
        $data = [];

        foreach (OrderStatus::cases() as $status) {
            $labels[] = $status->label();
            $data[] = (int) ($counts[$status->value] ?? 0);
        }

        return ['labels' => $labels, 'data' => $data, 'colors' => self::ORDER_STATUS_COLORS];
    }

    /**
     * @return array{labels: array<string>, data: array<int>}
     */
    public function paymentMethodBreakdown(?Carbon $from = null, ?Carbon $to = null): array
    {
        $query = Order::query();

        if ($from && $to) {
            $query->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()]);
        }

        $counts = $query->selectRaw('payment_method, COUNT(*) as count')
            ->groupBy('payment_method')
            ->pluck('count', 'payment_method');

        $labels = [];
        $data = [];

        foreach (PaymentMethod::cases() as $method) {
            $labels[] = $method->label();
            $data[] = (int) ($counts[$method->value] ?? 0);
        }

        return compact('labels', 'data');
    }

    public function topProducts(?Carbon $from = null, ?Carbon $to = null, int $limit = 5): Collection
    {
        return OrderItem::query()
            ->when($from && $to, fn ($q) => $q->whereHas('order', function ($q) use ($from, $to) {
                $q->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()])
                    ->where('order_status', '!=', OrderStatus::Cancelled->value);
            }))
            ->selectRaw('product_id, product_name, SUM(quantity) as total_quantity, SUM(subtotal) as total_revenue')
            ->groupBy('product_id', 'product_name')
            ->orderByDesc('total_quantity')
            ->limit($limit)
            ->get();
    }

    public function categoryPerformance(Carbon $from, Carbon $to, int $limit = 6): Collection
    {
        return DB::table('order_items')
            ->join('orders', 'orders.id', '=', 'order_items.order_id')
            ->join('products', 'products.id', '=', 'order_items.product_id')
            ->join('categories', 'categories.id', '=', 'products.category_id')
            ->where('orders.order_status', '!=', OrderStatus::Cancelled->value)
            ->whereBetween('orders.created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()])
            ->selectRaw('categories.name as category, SUM(order_items.subtotal) as revenue')
            ->groupBy('categories.id', 'categories.name')
            ->orderByDesc('revenue')
            ->limit($limit)
            ->get();
    }

    /**
     * @return array{total_orders: int, total_revenue: float, average_order_value: float}
     */
    public function summary(Carbon $from, Carbon $to): array
    {
        $base = Order::where('order_status', '!=', OrderStatus::Cancelled->value)
            ->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()]);

        $totalOrders = (clone $base)->count();
        $totalRevenue = (float) (clone $base)->sum('grand_total');
        $averageOrderValue = $totalOrders > 0 ? $totalRevenue / $totalOrders : 0.0;

        return [
            'total_orders' => $totalOrders,
            'total_revenue' => $totalRevenue,
            'average_order_value' => $averageOrderValue,
        ];
    }
}
