<?php

namespace App\Http\Controllers\Backend;

use App\Http\Controllers\Controller;
use App\Models\Product;
use App\Models\ProductImage;
use App\Models\Shop;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
use Intervention\Image\ImageManager;

class ProductController extends Controller
{
    /**
     * Display a listing of products for a shop.
     */
    public function index(Shop $shop)
    {
        $this->authorize('view', $shop);

        $products = $shop->products()->with('creator', 'images')->latest()->get();

        return view('backend.products.index', compact('shop', 'products'));
    }

    /**
     * Show the form for creating a new product.
     */
    public function create(Shop $shop)
    {
        $this->authorize('create', [Product::class, $shop->id]);

        return view('backend.products.create', compact('shop'));
    }

    /**
     * Store a newly created product in storage.
     */
    public function store(Request $request, Shop $shop)
    {
        $this->authorize('create', [Product::class, $shop->id]);

        $request->validate([
            'product_title' => 'required|string|max:255',
            'price' => 'required|numeric|min:0',
            'offer_price' => 'nullable|numeric|min:0',
            'offer_expiry_date' => 'nullable|date',
            'stock' => 'required|integer|min:0',
            'sku' => 'required|string|unique:products,sku',
            'feature_image' => 'required|image|mimes:webp,jpeg,png,jpg,gif|max:2048',
            'gallery_images' => 'nullable|array',
            'gallery_images.*' => 'image|mimes:jpeg,png,jpg,gif,webp|max:2048',
            'attributes' => 'nullable|json',
            'variant_inventory' => 'nullable|json',
        ]);

        try {
            // Prepare product data
            $data = $request->only(['product_title', 'price', 'offer_price', 'offer_expiry_date', 'sku']);
            $data['shop_id'] = $shop->id;
            $data['created_by'] = Auth::id();
            $data['attributes'] = $this->buildAttributesPayload($request);
            $data['stock'] = $this->resolveManagedStock($request, $data['attributes']);

            // Keep offer available all the time when no expiry is provided.
            if (! $request->filled('offer_price')) {
                $data['offer_expiry_date'] = null;
            }

            // Handle feature image
            if ($request->hasFile('feature_image')) {
                $data['feature_image'] = $this->compressAndStore($request->file('feature_image'), 'products/features');
            }

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

            // Handle gallery images
            if ($request->hasFile('gallery_images')) {
                foreach ($request->file('gallery_images') as $image) {
                    $imagePath = $this->compressAndStore($image, 'products/gallery');
                    ProductImage::create([
                        'product_id' => $product->id,
                        'image_path' => $imagePath,
                    ]);
                }
            }

            return redirect()->route('admin.shops.products.index', $shop)
                ->with('success', 'Product created successfully!');
        } catch (\Throwable $e) {
            return back()->withErrors('Error creating product: ' . $e->getMessage());
        }
    }

    /**
     * Display the specified product.
     */
    public function show(Shop $shop, Product $product)
    {
        abort_unless((int) $product->shop_id === (int) $shop->id, 404);

        $this->authorize('view', $product);

        return view('backend.products.show', compact('shop', 'product'));
    }

    /**
     * Show the form for editing the specified product.
     */
    public function edit(Shop $shop, Product $product)
    {
        abort_unless((int) $product->shop_id === (int) $shop->id, 404);

        $this->authorize('update', $product);

        return view('backend.products.edit', compact('shop', 'product'));
    }

    /**
     * Update the specified product in storage.
     */
    public function update(Request $request, Shop $shop, Product $product)
    {
        abort_unless((int) $product->shop_id === (int) $shop->id, 404);

        $this->authorize('update', $product);

        $request->validate([
            'product_title' => 'required|string|max:255',
            'price' => 'required|numeric|min:0',
            'offer_price' => 'nullable|numeric|min:0',
            'offer_expiry_date' => 'nullable|date',
            'stock' => 'required|integer|min:0',
            'sku' => 'required|string|unique:products,sku,' . $product->id,
            'feature_image' => 'nullable|image|mimes:webp,jpeg,png,jpg,gif|max:2048',
            'gallery_images' => 'nullable|array',
            'gallery_images.*' => 'image|mimes:webp,jpeg,png,jpg,gif|max:2048',
            'attributes' => 'nullable|json',
            'variant_inventory' => 'nullable|json',
        ]);

        try {
            // Update product data
            $data = $request->only(['product_title', 'price', 'offer_price', 'offer_expiry_date', 'sku']);
            $data['attributes'] = $this->buildAttributesPayload($request);
            $data['stock'] = $this->resolveManagedStock($request, $data['attributes']);

            // Keep offer available all the time when no expiry is provided.
            if (! $request->filled('offer_price')) {
                $data['offer_expiry_date'] = null;
            }

            // Handle feature image
            if ($request->hasFile('feature_image')) {
                // Delete old feature image
                if ($product->feature_image) {
                    Storage::disk('public')->delete($product->feature_image);
                }
                $data['feature_image'] = $this->compressAndStore($request->file('feature_image'), 'products/features');
            }

            // Update product
            $product->update($data);

            // Handle gallery images - delete old and add new if provided
            if ($request->hasFile('gallery_images')) {
                // Delete old gallery images
                foreach ($product->images as $image) {
                    Storage::disk('public')->delete($image->image_path);
                    $image->delete();
                }

                // Add new gallery images
                foreach ($request->file('gallery_images') as $image) {
                    $imagePath = $this->compressAndStore($image, 'products/gallery');
                    ProductImage::create([
                        'product_id' => $product->id,
                        'image_path' => $imagePath,
                    ]);
                }
            }

            return redirect()->route('admin.shops.products.index', $shop)
                ->with('success', 'Product updated successfully!');
        } catch (\Throwable $e) {
            return back()->withErrors('Error updating product: ' . $e->getMessage());
        }
    }

    /**
     * Remove the specified product from storage.
     */
    public function destroy(Shop $shop, Product $product)
    {
        abort_unless((int) $product->shop_id === (int) $shop->id, 404);

        $this->authorize('delete', $product);

        try {
            // Delete feature image
            if ($product->feature_image) {
                Storage::disk('public')->delete($product->feature_image);
            }

            // Delete gallery images
            foreach ($product->images as $image) {
                Storage::disk('public')->delete($image->image_path);
            }

            // Delete product
            $product->delete();

            return redirect()->route('admin.shops.products.index', $shop)
                ->with('success', 'Product deleted successfully!');
        } catch (\Throwable $e) {
            return back()->withErrors('Error deleting product: ' . $e->getMessage());
        }
    }

    /**
     * Compress and store an image file.
     */
    private function compressAndStore($file, $path)
    {
        try {
            $image = ImageManager::gd()->read($file)->orient();

            $image->scaleDown(1200, 1200);

            $filename = time() . '_' . uniqid() . '.webp';
            $fullPath = $path . '/' . $filename;

            if (!Storage::disk('public')->exists($path)) {
                Storage::disk('public')->makeDirectory($path);
            }

            Storage::disk('public')->put($fullPath, (string) $image->encodeByExtension('webp', 80));

            return $fullPath;
        } catch (\Throwable $e) {
            return $file->store($path, 'public');
        }
    }

    private function buildAttributesPayload(Request $request): array
    {
        $attributes = $this->normalizeAttributes($this->decodeJsonInput($request->input('attributes')));
        $variantInventory = $this->normalizeVariantInventory($this->decodeJsonInput($request->input('variant_inventory')));

        if ($variantInventory !== []) {
            $attributes['_variant_inventory'] = $variantInventory;
        }

        return $attributes;
    }

    private function resolveManagedStock(Request $request, array $attributes): int
    {
        $variantInventory = $attributes['_variant_inventory'] ?? [];

        if (is_array($variantInventory) && $variantInventory !== []) {
            return (int) collect($variantInventory)->sum(fn(array $item) => max(0, (int) ($item['stock'] ?? 0)));
        }

        return max(0, (int) $request->input('stock', 0));
    }

    private function normalizeAttributes(array $attributes): array
    {
        unset($attributes['_variant_inventory']);

        $normalized = [];

        foreach ($attributes as $key => $rawValues) {
            $name = trim((string) $key);
            if ($name === '') {
                continue;
            }

            $values = is_array($rawValues) ? $rawValues : [$rawValues];
            $values = array_values(array_unique(array_filter(array_map(function ($value) {
                if (is_array($value)) {
                    $value = $value['name'] ?? $value['value'] ?? $value['label'] ?? null;
                }

                return trim((string) $value);
            }, $values))));

            if ($values !== []) {
                $normalized[$name] = $values;
            }
        }

        return $normalized;
    }

    private function normalizeVariantInventory(array $inventory): array
    {
        $normalized = [];
        $seen = [];

        foreach ($inventory as $item) {
            if (! is_array($item)) {
                continue;
            }

            $label = trim((string) ($item['label'] ?? $item['name'] ?? $item['value'] ?? ''));
            if ($label === '') {
                continue;
            }

            $signature = Str::lower($label);
            if (isset($seen[$signature])) {
                continue;
            }

            $seen[$signature] = true;
            $normalized[] = [
                'label' => $label,
                'stock' => max(0, (int) ($item['stock'] ?? 0)),
            ];
        }

        return $normalized;
    }

    private function decodeJsonInput(mixed $value): array
    {
        if (is_array($value)) {
            return $value;
        }

        if (! is_string($value) || trim($value) === '') {
            return [];
        }

        $decoded = json_decode($value, true);

        return is_array($decoded) ? $decoded : [];
    }
}
