<?php

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\DiscountCode;
use App\Http\Controllers\Api\StockNotificationController;
use App\Models\EmailSubscriber;
use App\Models\Order;
use App\Models\Product;
use App\Models\ProductCategory;
use App\Models\ProductImage;
use App\Models\ProductVariant;
use App\Models\User;
use Illuminate\Http\JsonResponse;
use Illuminate\Support\Facades\Cache;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;

class AdminController extends Controller
{
    // ── Dashboard ────────────────────────────────────────────────────────────

    public function dashboard(): JsonResponse
    {
        $totalRevenue = Order::where('status', '!=', 'cancelled')->sum('total');
        $totalOrders  = Order::count();
        $totalCustomers = User::role('customer')->count();
        $totalProducts  = Product::where('status', 'active')->count();

        $revenueToday = Order::whereDate('created_at', today())
            ->where('status', '!=', 'cancelled')
            ->sum('total');

        $ordersToday = Order::whereDate('created_at', today())->count();

        $recentOrders = Order::with(['user', 'shippingAddress'])
            ->orderBy('created_at', 'desc')
            ->limit(10)
            ->get()
            ->map(fn($o) => [
                'id'           => $o->id,
                'order_number' => $o->order_number,
                'customer'     => $o->user?->name ?? $o->guest_email ?? 'Guest',
                'total'        => $o->total,
                'status'       => $o->status,
                'created_at'   => $o->created_at,
            ]);

        $revenueByDay = Order::where('status', '!=', 'cancelled')
            ->where('created_at', '>=', now()->subDays(30))
            ->selectRaw('DATE(created_at) as date, SUM(total) as revenue, COUNT(*) as orders')
            ->groupBy('date')
            ->orderBy('date')
            ->get();

        $lowStock = Product::where('track_stock', true)
            ->where('stock_quantity', '<=', 10)
            ->where('status', 'active')
            ->select('id', 'name', 'slug', 'stock_quantity')
            ->limit(5)
            ->get();

        return response()->json([
            'stats' => [
                'total_revenue'   => round($totalRevenue, 2),
                'total_orders'    => $totalOrders,
                'total_customers' => $totalCustomers,
                'total_products'  => $totalProducts,
                'revenue_today'   => round($revenueToday, 2),
                'orders_today'    => $ordersToday,
            ],
            'recent_orders'  => $recentOrders,
            'revenue_by_day' => $revenueByDay,
            'low_stock'      => $lowStock,
        ]);
    }

    // ── Public tracking config ───────────────────────────────────────────────

    public function publicTrackingConfig(): JsonResponse
    {
        $settings = DB::table('store_settings')->pluck('value', 'key');
        return response()->json([
            'ga4_measurement_id'        => $settings->get('ga4_measurement_id', ''),
            'google_ads_conversion_id'  => $settings->get('google_ads_conversion_id', ''),
            'meta_pixel_id'             => $settings->get('meta_pixel_id', ''),
            'tiktok_pixel_id'           => $settings->get('tiktok_pixel_id', ''),
        ]);
    }

    // ── Analytics ────────────────────────────────────────────────────────────

    public function analyticsChannels(Request $request): JsonResponse
    {
        $from = $request->query('from');
        $to   = $request->query('to');

        if ($from && $to) {
            $base = Order::where('status', '!=', 'cancelled')
                ->whereDate('created_at', '>=', $from)
                ->whereDate('created_at', '<=', $to);
        } else {
            $days = (int) $request->query('days', 30);
            $days = in_array($days, [7, 30, 90]) ? $days : 30;
            $base = Order::where('status', '!=', 'cancelled')
                ->where('created_at', '>=', now()->subDays($days));
        }

        // Channel attribution: map utm_source/medium to readable channel
        $orders = (clone $base)
            ->selectRaw('
                utm_source,
                utm_medium,
                utm_campaign,
                COUNT(*) as orders,
                SUM(total) as revenue,
                AVG(total) as avg_order
            ')
            ->groupBy('utm_source', 'utm_medium', 'utm_campaign')
            ->orderByDesc('revenue')
            ->get()
            ->map(function ($row) {
                $source = strtolower($row->utm_source ?? '');
                $medium = strtolower($row->utm_medium ?? '');

                if (!$source && !$medium) {
                    $channel = 'Direct';
                } elseif (str_contains($medium, 'cpc') || str_contains($medium, 'paid') || str_contains($medium, 'ppc')) {
                    if (str_contains($source, 'google')) $channel = 'Google Ads';
                    elseif (str_contains($source, 'facebook') || str_contains($source, 'fb') || str_contains($source, 'instagram')) $channel = 'Meta Ads';
                    elseif (str_contains($source, 'tiktok')) $channel = 'TikTok Ads';
                    else $channel = 'Paid Search';
                } elseif ($medium === 'organic' || (!$medium && $source)) {
                    if (str_contains($source, 'google')) $channel = 'Google Organic';
                    elseif (str_contains($source, 'bing')) $channel = 'Bing Organic';
                    else $channel = 'Organic Search';
                } elseif ($medium === 'social' || str_contains($source, 'facebook') || str_contains($source, 'instagram') || str_contains($source, 'tiktok')) {
                    $channel = 'Social';
                } elseif ($medium === 'email') {
                    $channel = 'Email';
                } elseif ($medium === 'referral') {
                    $channel = 'Referral';
                } else {
                    $channel = ucfirst($source ?: 'Other');
                }

                return [
                    'channel'    => $channel,
                    'source'     => $row->utm_source,
                    'medium'     => $row->utm_medium,
                    'campaign'   => $row->utm_campaign,
                    'orders'     => (int) $row->orders,
                    'revenue'    => round((float) $row->revenue, 2),
                    'avg_order'  => round((float) $row->avg_order, 2),
                ];
            });

        // Aggregate by channel
        $byChannel = $orders->groupBy('channel')->map(function ($rows, $channel) {
            return [
                'channel'   => $channel,
                'orders'    => $rows->sum('orders'),
                'revenue'   => round($rows->sum('revenue'), 2),
                'avg_order' => $rows->count() ? round($rows->avg('avg_order'), 2) : 0,
                'breakdown' => $rows->values(),
            ];
        })->sortByDesc('revenue')->values();

        // Revenue by day per top channel (for chart)
        $topChannels = $byChannel->take(5)->pluck('channel')->toArray();

        $totalRevenue = (clone $base)->sum('total');
        $totalOrders  = (clone $base)->count();

        return response()->json([
            'days'          => $days,
            'total_revenue' => round((float) $totalRevenue, 2),
            'total_orders'  => $totalOrders,
            'channels'      => $byChannel,
        ]);
    }

    // ── Orders ───────────────────────────────────────────────────────────────

    public function orders(Request $request): JsonResponse
    {
        $query = Order::with(['user', 'shippingAddress', 'items']);

        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }
        if ($request->filled('search')) {
            $s = $request->search;
            $query->where(function ($q) use ($s) {
                $q->where('order_number', 'like', "%$s%")
                  ->orWhere('guest_email', 'like', "%$s%")
                  ->orWhereHas('user', fn($u) => $u->where('name', 'like', "%$s%")->orWhere('email', 'like', "%$s%"));
            });
        }
        if ($request->filled('from')) {
            $query->whereDate('created_at', '>=', $request->from);
        }
        if ($request->filled('to')) {
            $query->whereDate('created_at', '<=', $request->to);
        }
        if ($request->filled('payment_method')) {
            $query->where('payment_method', $request->payment_method);
        }
        if ($request->filled('min_total')) {
            $query->where('total', '>=', (float) $request->min_total);
        }
        if ($request->filled('max_total')) {
            $query->where('total', '<=', (float) $request->max_total);
        }
        if ($request->boolean('preorder')) {
            $query->whereHas('items', fn($q) => $q->where('is_preorder', true));
        }

        $sort = $request->query('sort', 'newest');
        match ($sort) {
            'oldest'       => $query->orderBy('created_at', 'asc'),
            'total_high'   => $query->orderBy('total', 'desc'),
            'total_low'    => $query->orderBy('total', 'asc'),
            default        => $query->orderBy('created_at', 'desc'),
        };

        $orders = $query->paginate(20);

        return response()->json([
            'data' => $orders->map(fn($o) => [
                'id'           => $o->id,
                'order_number' => $o->order_number,
                'customer'     => $o->user?->name ?? $o->guest_email ?? 'Guest',
                'email'        => $o->user?->email ?? $o->guest_email,
                'total'        => $o->total,
                'status'       => $o->status,
                'payment_method' => $o->payment_method,
                'city'         => $o->shippingAddress?->city,
                'has_preorder' => $o->items->contains('is_preorder', true),
                'created_at'   => $o->created_at,
            ]),
            'meta' => [
                'current_page' => $orders->currentPage(),
                'last_page'    => $orders->lastPage(),
                'total'        => $orders->total(),
            ],
        ]);
    }

    public function order(int $id): JsonResponse
    {
        $order = Order::with(['user', 'items', 'shippingAddress', 'billingAddress'])
            ->findOrFail($id);

        return response()->json(['data' => $order]);
    }

    public function updateOrderStatus(Request $request, int $id): JsonResponse
    {
        $request->validate([
            'status'          => 'required|in:pending,paid,processing,shipped,delivered,cancelled,refunded',
            'tracking_number' => 'nullable|string|max:100',
        ]);

        $order = Order::findOrFail($id);
        $order->update([
            'status'          => $request->status,
            'tracking_number' => $request->tracking_number ?? $order->tracking_number,
        ]);

        return response()->json(['message' => 'Order updated.', 'data' => $order]);
    }

    // ── Products ─────────────────────────────────────────────────────────────

    public function products(Request $request): JsonResponse
    {
        $query = Product::with(['categories', 'primaryImage'])->orderBy('created_at', 'desc');

        if ($request->filled('search')) {
            $query->where('name', 'like', '%' . $request->search . '%');
        }
        if ($request->filled('status')) {
            $query->where('status', $request->status);
        }

        $products = $query->paginate(20);

        return response()->json([
            'data' => $products->items(),
            'meta' => [
                'current_page' => $products->currentPage(),
                'last_page'    => $products->lastPage(),
                'total'        => $products->total(),
            ],
        ]);
    }

    public function createProduct(Request $request): JsonResponse
    {
        $data = $request->validate([
            'name'              => 'required|string|max:255',
            'slug'              => 'nullable|string|unique:products,slug',
            'short_description' => 'nullable|string',
            'description'       => 'nullable|string',
            'price'             => 'required|numeric|min:0',
            'compare_price'     => 'nullable|numeric|min:0',
            'stock_quantity'    => 'required|integer|min:0',
            'track_stock'       => 'sometimes|boolean',
            'allow_preorder'    => 'sometimes|boolean',
            'preorder_note'     => 'nullable|string|max:255',
            'preorder_days'     => 'nullable|integer|min:1|max:365',
            'is_featured'       => 'sometimes|boolean',
            'is_bestseller'       => 'sometimes|boolean',
            'loyalty_points_earn' => 'nullable|integer|min:0',
            'status'              => 'sometimes|in:active,draft,archived',
            'weight_grams'        => 'nullable|integer',
            'meta_title'          => 'nullable|string|max:255',
            'meta_description'    => 'nullable|string|max:500',
            'category_ids'        => 'nullable|array',
        ]);

        $data['slug'] = $data['slug'] ?? Str::slug($data['name']);
        $data['weight_grams'] = $data['weight_grams'] ?? 0;

        $product = Product::create($data);

        if (!empty($data['category_ids'])) {
            $product->categories()->sync($data['category_ids']);
        }

        return response()->json(['data' => $product->load('categories')], 201);
    }

    public function updateProduct(Request $request, int $id): JsonResponse
    {
        $product = Product::findOrFail($id);

        $data = $request->validate([
            'name'              => 'sometimes|string|max:255',
            'slug'              => "sometimes|string|max:255|unique:products,slug,{$id}",
            'short_description' => 'nullable|string',
            'description'       => 'nullable|string',
            'price'             => 'sometimes|numeric|min:0',
            'compare_price'     => 'nullable|numeric|min:0',
            'stock_quantity'    => 'sometimes|integer|min:0',
            'track_stock'       => 'sometimes|boolean',
            'allow_preorder'    => 'sometimes|boolean',
            'preorder_note'     => 'nullable|string|max:255',
            'preorder_days'     => 'nullable|integer|min:1|max:365',
            'is_featured'       => 'sometimes|boolean',
            'is_bestseller'       => 'sometimes|boolean',
            'loyalty_points_earn' => 'nullable|integer|min:0',
            'status'              => 'sometimes|in:active,draft,archived',
            'weight_grams'        => 'nullable|integer',
            'meta_title'          => 'nullable|string|max:255',
            'meta_description'    => 'nullable|string|max:500',
            'category_ids'        => 'nullable|array',
        ]);

        // weight_grams column is NOT NULL in DB — coerce null to 0
        if (array_key_exists('weight_grams', $data)) {
            $data['weight_grams'] = $data['weight_grams'] ?? 0;
        }

        $wasOutOfStock = $product->stock_quantity <= 0;
        $product->update($data);

        if (array_key_exists('category_ids', $data)) {
            $product->categories()->sync($data['category_ids'] ?? []);
        }

        // Auto-notify subscribers when stock goes from 0 → positive
        if ($wasOutOfStock && isset($data['stock_quantity']) && $data['stock_quantity'] > 0) {
            StockNotificationController::autoTrigger($product->id);
        }

        return response()->json(['data' => $product->load('categories')]);
    }

    public function product(int $id): JsonResponse
    {
        $product = Product::with(['categories', 'images', 'variants'])->findOrFail($id);
        return response()->json(['data' => $product]);
    }

    public function deleteProduct(int $id): JsonResponse
    {
        Product::findOrFail($id)->delete();
        return response()->json(['message' => 'Product deleted.']);
    }

    // ── Product Images ───────────────────────────────────────────────────────

    public function uploadProductImage(Request $request, int $id): JsonResponse
    {
        $request->validate(['file' => 'required|image|max:5120']);
        $product = Product::findOrFail($id);

        $file = $request->file('file');
        $name = Str::uuid() . '.' . $file->getClientOriginalExtension();
        $file->move(public_path('uploads'), $name);

        $isPrimary = $product->images()->count() === 0;
        $image = $product->images()->create([
            'url'        => url('uploads/' . $name),
            'alt_text'   => $product->name,
            'sort_order' => $product->images()->count(),
            'is_primary' => $isPrimary,
        ]);

        return response()->json(['data' => $image], 201);
    }

    public function deleteProductImage(int $imageId): JsonResponse
    {
        $image = ProductImage::findOrFail($imageId);
        $wasPrimary = $image->is_primary;
        $productId = $image->product_id;
        $image->delete();

        if ($wasPrimary) {
            ProductImage::where('product_id', $productId)->oldest()->first()?->update(['is_primary' => true]);
        }

        return response()->json(['message' => 'Image deleted.']);
    }

    public function setProductImagePrimary(int $imageId): JsonResponse
    {
        $image = ProductImage::findOrFail($imageId);
        ProductImage::where('product_id', $image->product_id)->update(['is_primary' => false]);
        $image->update(['is_primary' => true]);
        return response()->json(['message' => 'Primary image set.']);
    }

    // ── Product Variants ─────────────────────────────────────────────────────

    public function createVariant(Request $request, int $id): JsonResponse
    {
        $product = Product::findOrFail($id);
        $data = $request->validate([
            'name'           => 'required|string|max:100',
            'sku'            => 'nullable|string|max:100',
            'price'          => 'required|numeric|min:0',
            'stock_quantity' => 'required|integer|min:0',
            'attributes'     => 'nullable|array',
        ]);
        $variant = $product->variants()->create($data);
        $product->updateQuietly(['stock_quantity' => $product->variants()->sum('stock_quantity')]);
        Cache::forget("product.{$product->slug}");
        return response()->json(['data' => $variant], 201);
    }

    public function updateVariant(Request $request, int $variantId): JsonResponse
    {
        $variant = ProductVariant::findOrFail($variantId);
        $data = $request->validate([
            'name'           => 'sometimes|string|max:100',
            'sku'            => 'nullable|string|max:100',
            'price'          => 'sometimes|numeric|min:0',
            'stock_quantity' => 'sometimes|integer|min:0',
            'attributes'     => 'nullable|array',
        ]);
        $wasOutOfStock = $variant->stock_quantity <= 0;
        $variant->update($data);

        // Keep product.stock_quantity in sync as the sum of all variant stocks
        // so listing pages (no variants loaded) also reflect correct is_in_stock
        if (isset($data['stock_quantity'])) {
            $totalVariantStock = $variant->product->variants()->sum('stock_quantity');
            $variant->product->updateQuietly(['stock_quantity' => $totalVariantStock]);
        }

        Cache::forget("product.{$variant->product->slug}");
        if ($wasOutOfStock && isset($data['stock_quantity']) && $data['stock_quantity'] > 0) {
            StockNotificationController::autoTrigger($variant->product_id, $variant->id);
        }
        return response()->json(['data' => $variant]);
    }

    public function deleteVariant(int $variantId): JsonResponse
    {
        $variant = ProductVariant::findOrFail($variantId);
        Cache::forget("product.{$variant->product->slug}");
        $variant->delete();
        return response()->json(['message' => 'Variant deleted.']);
    }

    // ── Customers ────────────────────────────────────────────────────────────

    public function customers(Request $request): JsonResponse
    {
        $query = User::role('customer')
            ->withCount('orders')
            ->orderBy('created_at', 'desc');

        if ($request->filled('search')) {
            $s = $request->search;
            $query->where(fn($q) => $q->where('name', 'like', "%$s%")->orWhere('email', 'like', "%$s%")->orWhere('phone', 'like', "%$s%"));
        }

        $customers = $query->paginate(20);

        return response()->json([
            'data' => $customers->map(fn($u) => [
                'id'                      => $u->id,
                'name'                    => $u->name,
                'email'                   => $u->email,
                'phone'                   => $u->phone,
                'loyalty_points'          => $u->loyalty_points,
                'orders_count'            => $u->orders_count,
                'age_verified'            => (bool) $u->age_verified_at,
                'email_verified'          => (bool) $u->email_verified_at,
                'email_marketing_consent' => (bool) $u->email_marketing_consent,
                'phone_verified'          => (bool) $u->phone_verified_at,
                'created_at'              => $u->created_at,
            ]),
            'meta' => [
                'current_page' => $customers->currentPage(),
                'last_page'    => $customers->lastPage(),
                'total'        => $customers->total(),
            ],
        ]);
    }

    public function customer(int $id): JsonResponse
    {
        $user = User::role('customer')->with([
            'orders' => fn($q) => $q->orderBy('created_at', 'desc')->limit(50),
            'orders.items',
        ])->withCount('orders')->findOrFail($id);

        $totalSpend = $user->orders->where('status', '!=', 'cancelled')->sum('total_amount');

        return response()->json([
            'data' => [
                'id'                      => $user->id,
                'name'                    => $user->name,
                'email'                   => $user->email,
                'phone'                   => $user->phone,
                'loyalty_points'          => $user->loyalty_points,
                'orders_count'            => $user->orders_count,
                'total_spend'             => round($totalSpend, 2),
                'age_verified'            => (bool) $user->age_verified_at,
                'age_verified_at'         => $user->age_verified_at,
                'email_verified'          => (bool) $user->email_verified_at,
                'email_marketing_consent' => (bool) $user->email_marketing_consent,
                'phone_verified'          => (bool) $user->phone_verified_at,
                'date_of_birth'           => $user->date_of_birth,
                'created_at'              => $user->created_at,
                'orders'                  => $user->orders->map(fn($o) => [
                    'id'           => $o->id,
                    'order_number' => $o->order_number,
                    'status'       => $o->status,
                    'total_amount' => $o->total_amount,
                    'created_at'   => $o->created_at,
                    'items_count'  => $o->items->count(),
                ]),
            ],
        ]);
    }

    // ── Discount Codes ───────────────────────────────────────────────────────

    public function discountCodes(): JsonResponse
    {
        $codes = DiscountCode::with('user:id,name,email')->orderBy('created_at', 'desc')->get();
        return response()->json(['data' => $codes]);
    }

    public function createDiscountCode(Request $request): JsonResponse
    {
        $data = $request->validate([
            'code'            => 'required|string|unique:discount_codes,code|max:50',
            'type'            => 'required|in:percentage,fixed',
            'value'           => 'required|numeric|min:0',
            'min_order_amount'=> 'nullable|numeric|min:0',
            'max_uses'        => 'nullable|integer|min:1',
            'expires_at'      => 'nullable|date',
            'is_active'       => 'boolean',
            'user_id'         => 'nullable|exists:users,id',
        ]);

        $data['code'] = strtoupper($data['code']);
        $code = DiscountCode::create($data);
        $code->load('user:id,name,email');

        return response()->json(['data' => $code], 201);
    }

    public function updateDiscountCode(Request $request, int $id): JsonResponse
    {
        $code = DiscountCode::findOrFail($id);
        $data = $request->validate([
            'is_active'  => 'boolean',
            'max_uses'   => 'nullable|integer|min:1',
            'expires_at' => 'nullable|date',
        ]);
        $code->update($data);
        return response()->json(['data' => $code]);
    }

    public function deleteDiscountCode(int $id): JsonResponse
    {
        DiscountCode::findOrFail($id)->delete();
        return response()->json(['message' => 'Discount code deleted.']);
    }

    // ── Subscribers ──────────────────────────────────────────────────────────

    public function subscribers(Request $request): JsonResponse
    {
        $subscribers = EmailSubscriber::whereNull('unsubscribed_at')
            ->orderBy('created_at', 'desc')
            ->get(['id', 'email', 'name', 'source', 'created_at']);

        return response()->json(['data' => $subscribers]);
    }

    // ── Collections (Product Categories) ────────────────────────────────────

    public function collections(): JsonResponse
    {
        $categories = ProductCategory::withCount('products')->orderBy('name')->get();
        return response()->json(['data' => $categories]);
    }

    public function createCollection(Request $request): JsonResponse
    {
        $data = $request->validate([
            'name'        => 'required|string|max:255',
            'slug'        => 'nullable|string|unique:product_categories,slug|max:255',
            'description' => 'nullable|string',
            'image'       => 'nullable|string',
        ]);
        $data['slug'] = $data['slug'] ?? Str::slug($data['name']);
        $category = ProductCategory::create($data);
        return response()->json(['data' => $category], 201);
    }

    public function updateCollection(Request $request, int $id): JsonResponse
    {
        $category = ProductCategory::findOrFail($id);
        $data = $request->validate([
            'name'        => 'sometimes|string|max:255',
            'description' => 'nullable|string',
            'image'       => 'nullable|string',
        ]);
        $category->update($data);
        return response()->json(['data' => $category]);
    }

    public function deleteCollection(int $id): JsonResponse
    {
        ProductCategory::findOrFail($id)->delete();
        return response()->json(['message' => 'Collection deleted.']);
    }

    // ── Store Settings ───────────────────────────────────────────────────────

    public function getSettings(): JsonResponse
    {
        $settings = DB::table('store_settings')->pluck('value', 'key');
        return response()->json(['data' => $settings]);
    }

    public function updateSettings(Request $request): JsonResponse
    {
        $allowed = [
            'store_name', 'store_email', 'store_phone', 'store_address',
            'currency', 'free_shipping_threshold', 'default_tax_rate',
            'facebook_url', 'instagram_url', 'twitter_url',
            'google_analytics_id', 'meta_title', 'meta_description',
            'payment_gateway',
            'exactly_api_key', 'exactly_merchant_id', 'exactly_webhook_secret',
            'fenopay_api_key', 'fenopay_merchant_id', 'fenopay_webhook_secret',
            'royalmail_api_key', 'royalmail_account_number',
            'mail_host', 'mail_port', 'mail_username', 'mail_password', 'mail_encryption', 'mail_from_address',
        ];

        foreach ($request->only($allowed) as $key => $value) {
            DB::table('store_settings')->updateOrInsert(
                ['key' => $key],
                ['value' => $value, 'updated_at' => now()]
            );
        }

        return response()->json(['message' => 'Settings saved.']);
    }
}
