<?php

namespace App\Http\Controllers;

use App\Models\Product;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Storage;
use App\Models\Stock;
use App\Models\Sale;

class ProductController extends Controller
{
    public function index(Request $request)
    {
        // If user is karyawan, use karyawanIndex logic
        if (auth()->user()->role !== 'admin') {
            return $this->karyawanIndex($request);
        }

        $branches = \App\Models\Branch::all();
        $selectedBranch = $request->input('branch', 'Total Gabungan');

        $query = Product::with(['stocks', 'recipes.rawMaterial']);
        
        if (auth()->user()->role !== 'admin') {
            $query->where('branch', auth()->user()->branch ?? 'Cabang 1');
        } elseif ($selectedBranch && $selectedBranch !== 'Total Gabungan') {
            $query->where('branch', $selectedBranch);
        }

        if ($request->filled('search')) {
            $query->where(function($q) use ($request) {
                $q->where('name', 'like', '%' . $request->search . '%')
                  ->orWhere('category', 'like', '%' . $request->search . '%');
            });
        }
        
        $products = $query->get();

        $selectedRawMaterialBranch = $request->input('raw_material_branch', 'Total Gabungan');
        $rawMaterialsQuery = \App\Models\RawMaterial::query();
        if ($selectedRawMaterialBranch && $selectedRawMaterialBranch !== 'Total Gabungan') {
            $rawMaterialsQuery->where('branch', $selectedRawMaterialBranch);
        }
        $rawMaterials = $rawMaterialsQuery->get();

        return view('produk', compact('products', 'branches', 'selectedBranch', 'rawMaterials', 'selectedRawMaterialBranch'));
    }

    public function store(Request $request)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'category' => 'required|in:makanan,minuman,snack',
            'purchase_price' => 'required|integer',
            'selling_price' => 'required|integer',
            'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
        ]);

        $data = $request->all();
        $branch = auth()->user()->branch ?? 'Cabang 1';
        $data['branch'] = $branch;

        // Unique name check per branch
        $exists = Product::whereRaw('LOWER(name) = ?', [strtolower($request->name)])
                         ->where('branch', $branch)
                         ->exists();
        if ($exists) {
            return back()->withInput()->withErrors(['duplicate_error' => 'Produk dengan nama "' . $request->name . '" sudah ada di ' . $branch . '.']);
        }

        if ($request->hasFile('image')) {
            $file = $request->file('image');
            $filename = time() . '_' . preg_replace('/[^a-zA-Z0-9._-]/', '', $file->getClientOriginalName());
            $destination = public_path('images/produk');
            if (!file_exists($destination)) {
                mkdir($destination, 0775, true);
            }
            $file->move($destination, $filename);
            $data['image'] = '/images/produk/' . $filename;
        }

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

        // Save Recipes if any
        if ($request->has('recipes') && is_array($request->recipes)) {
            foreach ($request->recipes as $recipeData) {
                if (isset($recipeData['raw_material_id']) && isset($recipeData['quantity_needed']) && $recipeData['quantity_needed'] !== '') {
                    // Parse quantity string to allow '300 ml', '0.5', '300'
                    $parsed = $this->parseQuantity($recipeData['quantity_needed']);
                    $rm = \App\Models\RawMaterial::find($recipeData['raw_material_id']);
                    $quantityToStore = $parsed['value'];
                    // If unit provided and differs from raw material unit, try to convert
                    if ($parsed['unit'] && $rm) {
                        $quantityToStore = $this->convertRecipeToRawMaterialUnit($parsed['value'], $parsed['unit'], $rm->unit);
                    }

                    \App\Models\ProductRecipe::create([
                        'product_id' => $product->id,
                        'raw_material_id' => $recipeData['raw_material_id'],
                        'quantity_needed' => $quantityToStore,
                    ]);
                }
            }

            // Recalculate stock based on raw materials
            $this->recalculateProductsFromRawMaterials($product->id, $branch);
        }

        // Create initial stock for THIS branch only
        \App\Models\Stock::create([
            'product_id' => $product->id,
            'branch' => $branch,
            'quantity' => 0,
            'unit' => 'Pcs',
        ]);

        return back()->with('success', 'Produk Master berhasil ditambahkan!');
    }

    public function update(Request $request, $id)
    {
        $request->validate([
            'name' => 'required|string|max:255',
            'category' => 'required|in:makanan,minuman,snack',
            'purchase_price' => 'required|integer',
            'selling_price' => 'required|integer',
            'image' => 'nullable|image|mimes:jpeg,png,jpg,gif,svg|max:2048',
        ]);

        $product = Product::findOrFail($id);
        $branch = auth()->user()->branch ?? 'Cabang 1';
        
        // Only allow update if product belongs to user's branch
        if ($product->branch !== $branch) {
            return back()->with('error', 'Anda tidak memiliki akses untuk mengubah produk dari cabang lain.');
        }
        
        // Unique name check for update per branch
        $exists = Product::whereRaw('LOWER(name) = ?', [strtolower($request->name)])
                         ->where('branch', $branch)
                         ->where('id', '!=', $id)
                         ->exists();
        if ($exists) {
            return back()->withInput()->withErrors(['duplicate_error' => 'Produk dengan nama "' . $request->name . '" sudah ada di ' . $branch . '.']);
        }

        $data = $request->only(['name', 'category', 'purchase_price', 'selling_price']);

        if ($request->hasFile('image')) {
            // Delete old image if it exists in our public/images/produk directory
            if ($product->image && str_contains($product->image, '/images/produk/')) {
                $oldFile = public_path($product->image);
                if (file_exists($oldFile)) {
                    unlink($oldFile);
                }
            }
            // Also handle old images stored via storage symlink
            if ($product->image && str_contains($product->image, '/storage/produk/')) {
                $oldPath = str_replace('/storage/', '', $product->image);
                Storage::disk('public')->delete($oldPath);
            }

            $file = $request->file('image');
            $filename = time() . '_' . preg_replace('/[^a-zA-Z0-9._-]/', '', $file->getClientOriginalName());
            $destination = public_path('images/produk');
            if (!file_exists($destination)) {
                mkdir($destination, 0775, true);
            }
            $file->move($destination, $filename);
            $data['image'] = '/images/produk/' . $filename;
        }

        $product->update($data);

        // Update Recipes
        \App\Models\ProductRecipe::where('product_id', $product->id)->delete();
        if ($request->has('recipes') && is_array($request->recipes)) {
            foreach ($request->recipes as $recipeData) {
                if (isset($recipeData['raw_material_id']) && isset($recipeData['quantity_needed']) && $recipeData['quantity_needed'] !== '') {
                    $parsed = $this->parseQuantity($recipeData['quantity_needed']);
                    $rm = \App\Models\RawMaterial::find($recipeData['raw_material_id']);
                    $quantityToStore = $parsed['value'];
                    if ($parsed['unit'] && $rm) {
                        $quantityToStore = $this->convertRecipeToRawMaterialUnit($parsed['value'], $parsed['unit'], $rm->unit);
                    }

                    \App\Models\ProductRecipe::create([
                        'product_id' => $product->id,
                        'raw_material_id' => $recipeData['raw_material_id'],
                        'quantity_needed' => $quantityToStore,
                    ]);
                }
            }

            // Recalculate stock based on raw materials
            $this->recalculateProductsFromRawMaterials($product->id, $branch);
        }

        return back()->with('success', 'Produk berhasil diperbarui!');
    }

    public function destroy($id)
    {
        $product = Product::findOrFail($id);
        $branch = auth()->user()->branch ?? 'Cabang 1';
        
        // Only allow deletion if product belongs to user's branch
        if ($product->branch !== $branch) {
            return back()->with('error', 'Anda tidak memiliki akses untuk menghapus produk dari cabang lain.');
        }
        
        $product->delete();
        return back()->with('success', 'Produk berhasil dihapus!');
    }

    // Karyawan: lihat produk dengan stok per cabang dan catat penjualan sederhana
    public function karyawanIndex(Request $request)
    {
        $branch = auth()->user()->branch ?? 'Cabang 1';
        $query = Product::with(['stocks' => function($q) use ($branch) {
            $q->where('branch', $branch);
        }, 'recipes.rawMaterial'])->where('branch', $branch);

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

        $products = $query->get();
        $rawMaterials = \App\Models\RawMaterial::where('branch', $branch)->get();

        return view('produk_karyawan', compact('products', 'branch', 'rawMaterials'));
    }

    public function sell(Request $request, $id)
    {
        $request->validate([
            'quantity' => 'required|integer|min:1'
        ]);

        $branch = auth()->user()->branch ?? 'Cabang 1';
        $qty = (int) $request->input('quantity');

        $product = Product::with('recipes.rawMaterial')->findOrFail($id);

        if ($product->recipes->count() > 0) {
            // Check raw material stock first
            foreach ($product->recipes as $recipe) {
                $rm = $recipe->rawMaterial;
                if (!$rm) continue;
                $needed = $recipe->quantity_needed * $qty;
                if ($rm->stock_quantity < $needed) {
                    return back()->withErrors(['stock' => 'Bahan baku tidak cukup untuk membuat ' . $qty . ' porsi.']);
                }
            }

            // Deduct raw materials
            foreach ($product->recipes as $recipe) {
                $rm = $recipe->rawMaterial;
                if ($rm) {
                    $rm->stock_quantity -= ($recipe->quantity_needed * $qty);
                    $rm->save();
                    // Optional: recalculate other products that depend on this raw material
                    $this->recalculateProductsFromRawMaterial($rm->id, $branch);
                }
            }
        } else {
            // Deduct from regular stock
            $stock = Stock::where('product_id', $id)->where('branch', $branch)->first();
            if (!$stock) {
                return back()->withErrors(['stock' => 'Stok untuk produk ini tidak ditemukan pada cabang Anda.']);
            }

            if ($stock->quantity < $qty) {
                return back()->withErrors(['stock' => 'Stok tidak cukup. Tersedia: ' . $stock->quantity]);
            }

            // Kurangi stok
            $stock->quantity = $stock->quantity - $qty;
            $stock->save();
        }

        // Catat penjualan sederhana
        Sale::create([
            'date' => now()->toDateString(),
            'branch' => $branch,
            'product_id' => $product->id,
            'item' => $product->name,
            'quantity' => $qty,
            'total_price' => ($product->selling_price ?? 0) * $qty,
        ]);

        return back()->with('success', 'Penjualan berhasil dicatat dan stok diperbarui.');
    }

    private function recalculateProductsFromRawMaterials($productId, $branch)
    {
        $product = \App\Models\Product::with('recipes.rawMaterial')->find($productId);
        if (!$product || $product->recipes->isEmpty()) {
            return;
        }

        $minStock = null;
        foreach ($product->recipes as $recipe) {
            $rm = $recipe->rawMaterial;
            if (!$rm || $recipe->quantity_needed <= 0) {
                continue;
            }
            $possibleAmount = floor($rm->stock_quantity / $recipe->quantity_needed);
            if ($minStock === null || $possibleAmount < $minStock) {
                $minStock = $possibleAmount;
            }
        }

        if ($minStock !== null && $minStock >= 0) {
            $stock = \App\Models\Stock::firstOrCreate(
                ['product_id' => $productId, 'branch' => $branch],
                ['quantity' => 0, 'unit' => 'Pcs']
            );
            $stock->quantity = $minStock;
            $stock->save();
        }
    }

    // Parse quantity strings used in recipe inputs (e.g. '300 ml', '0.5', '300')
    private function parseQuantity($quantityStr)
    {
        $quantityStr = trim((string)$quantityStr);
        if (preg_match('/^([\d.,]+)\s*(.*)$/', $quantityStr, $m)) {
            $value = floatval(str_replace(',', '.', $m[1]));
            $unit = strtolower(trim($m[2]));
            return ['value' => $value, 'unit' => $unit];
        }
        return ['value' => floatval($quantityStr), 'unit' => ''];
    }

    // Convert a recipe quantity with unit into the raw material's unit if possible
    private function convertRecipeToRawMaterialUnit($value, $fromUnit, $toUnit)
    {
        $from = strtolower(trim($fromUnit));
        $to = strtolower(trim($toUnit));

        // If same unit or no fromUnit, return value as-is
        if (!$from || $from === $to) return $value;

        // Basic conversions: liters <-> ml, kilogram <-> gram
        $mapping = [
            'l' => ['ml' => 1000, 'liter' => 1000],
            'liter' => ['ml' => 1000, 'l' => 1],
            'ml' => ['l' => 1/1000, 'liter' => 1/1000],
            'kg' => ['g' => 1000, 'kilogram' => 1000],
            'kilogram' => ['g' => 1000, 'kg' => 1],
            'g' => ['kg' => 1/1000, 'gram' => 1/1000],
            'gram' => ['kg' => 1/1000, 'g' => 1],
        ];

        // Normalize tokens
        $aliases = [
            'grams' => 'g', 'gram' => 'g', 'gr' => 'g',
            'milliliter' => 'ml', 'mililiter' => 'ml',
            'litre' => 'l', 'liters' => 'l', 'liter' => 'l',
            'kilograms' => 'kg', 'kilogram' => 'kg',
            'pcs' => 'pcs', 'piece' => 'pcs', 'pc' => 'pcs',
            'botol' => 'botol'
        ];

        $fromNorm = $aliases[$from] ?? $from;
        $toNorm = $aliases[$to] ?? $to;

        // If one is pcs and other is numeric unit, do not convert automatically
        if (($fromNorm === 'pcs' && $toNorm !== 'pcs') || ($toNorm === 'pcs' && $fromNorm !== 'pcs')) {
            return $value;
        }

        if (isset($mapping[$fromNorm]) && isset($mapping[$fromNorm][$toNorm])) {
            return $value * $mapping[$fromNorm][$toNorm];
        }

        // If direct mapping not found, try inverse
        if (isset($mapping[$toNorm]) && isset($mapping[$toNorm][$fromNorm])) {
            return $value / $mapping[$toNorm][$fromNorm];
        }

        return $value;
    }

    private function recalculateProductsFromRawMaterial($rawMaterialId, $branch)
    {
        $recipes = \App\Models\ProductRecipe::where('raw_material_id', $rawMaterialId)
            ->whereHas('product', function($q) use ($branch) {
                $q->where('branch', $branch);
            })->get();
        foreach ($recipes as $recipe) {
            $product = clone $recipe->product;
            
            $minStock = null;
            foreach ($product->recipes as $prodRecipe) {
                $rm = $prodRecipe->rawMaterial;
                $possibleAmount = floor($rm->stock_quantity / $prodRecipe->quantity_needed);
                if ($minStock === null || $possibleAmount < $minStock) {
                    $minStock = $possibleAmount;
                }
            }
            
            if ($minStock !== null) {
                $stock = \App\Models\Stock::firstOrCreate(
                    ['product_id' => $product->id, 'branch' => $branch],
                    ['quantity' => 0, 'unit' => 'Pcs']
                );
                $stock->quantity = $minStock;
                $stock->save();
            }
        }
    }
    public function updateStock(Request $request, $id)
    {
        $request->validate([
            'quantity' => 'required|integer'
        ]);

        $branch = auth()->user()->branch ?? 'Cabang 1';

        $stock = Stock::where('product_id', $id)->where('branch', $branch)->first();
        if (!$stock) {
            return back()->withErrors(['stock' => 'Stok untuk produk ini tidak ditemukan pada cabang Anda.']);
        }

        $stock->quantity += (int)$request->input('quantity');
        if ($stock->quantity < 0) $stock->quantity = 0;
        $stock->save();

        return back()->with('success', 'Stok berhasil diperbarui.');
    }
}
