<?php

namespace App\Http\Controllers\Admin;

use App\Http\Controllers\Controller;
use App\Models\Order;
use App\Models\OrderItem;
use App\Models\Product;
use App\Models\ProductVariant;
use App\Models\SiteSetting;
use Illuminate\Http\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\View\View;
use Symfony\Component\HttpFoundation\StreamedResponse;

class ReportController extends Controller
{
    private const LOW_STOCK_THRESHOLD = 5;

    /**
     * @var list<string>
     */
    private array $salesStatuses = [
        Order::STATUS_APPROVED,
        Order::STATUS_PROCESSING,
        Order::STATUS_IN_HAND_DELIVERY,
        Order::STATUS_REVIEW_AND_RATING,
        Order::STATUS_COMPLETED,
    ];

    public function index(Request $request): View
    {
        [$from, $to] = $this->dateRange($request);
        $threshold = max(0, min(999999, $request->integer('low_stock_threshold', self::LOW_STOCK_THRESHOLD)));

        $orders = Order::query()
            ->whereIn('status', $this->salesStatuses)
            ->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()]);

        $monthlyRevenue = Order::query()
            ->whereIn('status', $this->salesStatuses)
            ->where('created_at', '>=', now()->subMonths(11)->startOfMonth())
            ->get(['total', 'created_at'])
            ->groupBy(fn (Order $order) => $order->created_at->format('Y-m'))
            ->map(fn (Collection $orders) => (float) $orders->sum('total'));

        $monthRows = collect(range(11, 0))
            ->map(function (int $monthsAgo) use ($monthlyRevenue): array {
                $month = now()->subMonths($monthsAgo);
                $key = $month->format('Y-m');
                $revenue = $monthlyRevenue->get($key, 0.0);

                return [
                    'label' => $month->format('M Y'),
                    'revenue' => $revenue,
                    'revenue_display' => SiteSetting::formatPrice($revenue),
                ];
            });

        return view('admin.reports.index', [
            'from' => $from,
            'to' => $to,
            'lowStockThreshold' => $threshold,
            'metrics' => [
                ['label' => 'Sales Revenue', 'value' => SiteSetting::formatPrice((float) (clone $orders)->sum('total')), 'note' => 'Approved and active orders'],
                ['label' => 'Orders', 'value' => number_format((clone $orders)->count()), 'note' => 'In selected date range'],
                ['label' => 'Average Order', 'value' => SiteSetting::formatPrice((float) ((clone $orders)->avg('total') ?? 0)), 'note' => 'Revenue per order'],
                ['label' => 'Completed', 'value' => number_format((clone $orders)->where('status', Order::STATUS_COMPLETED)->count()), 'note' => 'Completed orders'],
            ],
            'bestSellingProducts' => $this->bestSellingProducts($from, $to),
            'monthlyRevenue' => $monthRows,
            'maxMonthlyRevenue' => max(1, (float) $monthRows->max('revenue')),
            'lowStockItems' => $this->lowStockItems($threshold),
        ]);
    }

    public function exportLowStock(Request $request): StreamedResponse
    {
        $threshold = max(0, min(999999, $request->integer('low_stock_threshold', self::LOW_STOCK_THRESHOLD)));
        $items = $this->lowStockItems($threshold);

        return response()->streamDownload(function () use ($items): void {
            $handle = fopen('php://output', 'w');
            fputcsv($handle, ['Type', 'Product', 'Variant', 'Sale Price', 'Purchase Price', 'Stock']);

            foreach ($items as $item) {
                fputcsv($handle, [
                    $item['type'],
                    $item['product'],
                    $item['variant'],
                    $item['sale_price'],
                    $item['purchase_price'],
                    $item['stock'],
                ]);
            }

            fclose($handle);
        }, 'low-stock-report.csv', ['Content-Type' => 'text/csv']);
    }

    /**
     * @return array{0: Carbon, 1: Carbon}
     */
    private function dateRange(Request $request): array
    {
        $from = $request->filled('from')
            ? Carbon::parse($request->string('from')->toString())
            : now()->startOfMonth();
        $to = $request->filled('to')
            ? Carbon::parse($request->string('to')->toString())
            : now();

        if ($from->gt($to)) {
            [$from, $to] = [$to, $from];
        }

        return [$from, $to];
    }

    /**
     * @return Collection<int, array<string, mixed>>
     */
    private function bestSellingProducts(Carbon $from, Carbon $to): Collection
    {
        return OrderItem::query()
            ->select([
                'product_id',
                'product_name',
                DB::raw('SUM(quantity) as units_sold'),
                DB::raw('SUM(line_total) as revenue'),
            ])
            ->whereHas('order', fn ($query) => $query
                ->whereIn('status', $this->salesStatuses)
                ->whereBetween('created_at', [$from->copy()->startOfDay(), $to->copy()->endOfDay()]))
            ->groupBy('product_id', 'product_name')
            ->orderByDesc('units_sold')
            ->limit(10)
            ->get()
            ->map(fn (OrderItem $item) => [
                'product_id' => $item->product_id,
                'name' => $item->product_name,
                'units_sold' => (int) $item->units_sold,
                'revenue' => (float) $item->revenue,
                'revenue_display' => SiteSetting::formatPrice((float) $item->revenue),
            ]);
    }

    /**
     * @return Collection<int, array<string, mixed>>
     */
    private function lowStockItems(int $threshold): Collection
    {
        $products = Product::query()
            ->where('stock', '<=', $threshold)
            ->orderBy('stock')
            ->get()
            ->map(fn (Product $product) => [
                'type' => 'Product',
                'product' => $product->name,
                'variant' => 'Base stock',
                'sale_price' => SiteSetting::formatPrice($product->sellingPrice()),
                'purchase_price' => SiteSetting::formatPrice((float) $product->purchase_price),
                'stock' => (int) $product->stock,
            ]);

        $variants = ProductVariant::query()
            ->with('product')
            ->where('stock', '<=', $threshold)
            ->orderBy('stock')
            ->get()
            ->map(fn (ProductVariant $variant) => [
                'type' => 'Variant',
                'product' => $variant->product->name,
                'variant' => $variant->label(),
                'sale_price' => SiteSetting::formatPrice($variant->effectivePrice()),
                'purchase_price' => SiteSetting::formatPrice((float) ($variant->purchase_price ?? $variant->product->purchase_price)),
                'stock' => (int) $variant->stock,
            ]);

        return collect($variants->all())
            ->merge($products->all())
            ->sortBy('stock')
            ->values();
    }
}
