<?php

namespace App\Livewire\PurchaseOrders;

use App\Models\Location;
use App\Models\Product;
use App\Models\PurchaseOrder;
use App\Models\PurchaseOrderItem;
use App\Models\Setting;
use App\Models\Partner;
use Livewire\Attributes\Computed;
use Livewire\Component;

class PurchaseOrderForm extends Component
{
    public ?int $orderId = null;
    public ?PurchaseOrder $order = null;

    // Form fields
    public ?int $partner_id = null;
    public ?int $locationId = null;
    public string $order_date = '';
    public string $expected_date = '';
    public string $notes = '';
    public string $status = 'draft';

    // Items
    public array $items = [];
    
    // Modals
    public bool $showProductModal = false;
    public string $productSearch = '';

    protected $listeners = ['productSelected'];

    protected function rules()
    {
        return [
            'partner_id' => 'required|exists:partners,id',
            'order_date' => 'required|date',
            'expected_date' => 'nullable|date|after_or_equal:order_date',
            'notes' => 'nullable|string',
            'items' => 'required|array|min:1',
            'items.*.product_id' => 'required|exists:products,id',
            'items.*.quantity' => 'required|numeric|min:1',
            'items.*.cost_price' => 'required|numeric|min:0',
        ];
    }

    #[Computed]
    public function suppliers()
    {
        return Partner::suppliers()->active()->orderBy('name')->get();
    }

    #[Computed]
    public function locations()
    {
        $branchId = auth()->user()->branch_id ?? 1;
        return Location::where('branch_id', $branchId)->active()->orderBy('sort_order')->get();
    }

    public function mount($id = null)
    {
        $branchId = auth()->user()->branch_id ?? 1;
        $defaultLocation = Location::where('branch_id', $branchId)->active()->where('is_default', true)->first();
        $this->locationId = $defaultLocation?->id;

        if ($id) {
            $this->orderId = $id;
            $this->order = PurchaseOrder::with('items.product')->findOrFail($id);
            $this->loadOrder();
        } else {
            $this->order_date = now()->format('Y-m-d');
        }
    }

    protected function loadOrder()
    {
        $this->partner_id = $this->order->partner_id;
        $this->locationId = $this->order->location_id ?? $this->locationId;
        $this->order_date = $this->order->order_date ? $this->order->order_date->format('Y-m-d') : now()->format('Y-m-d');
        $this->expected_date = $this->order->expected_date ? $this->order->expected_date->format('Y-m-d') : '';
        $this->notes = $this->order->notes ?? '';
        $this->status = $this->order->status;

        $this->items = [];
        foreach ($this->order->items as $item) {
            $this->items[] = [
                'id' => $item->id,
                'product_id' => $item->product_id,
                'product_name' => $item->product->localized_name,
                'sku' => $item->product->sku,
                'quantity' => $item->quantity,
                'received_qty' => $item->received_qty,
                'cost_price' => $item->cost_price,
                'total' => $item->quantity * $item->cost_price,
                'batch_no' => $item->batch_no ?? '',
                'expiry_date' => $item->expiry_date ? $item->expiry_date->format('Y-m-d') : '',
            ];
        }
    }

    public function openProductModal()
    {
        $this->productSearch = '';
        $this->showProductModal = true;
    }

    public function addProduct(int $productId)
    {
        $product = Product::find($productId);
        if (!$product) return;

        // Check if product already in items
        foreach ($this->items as $index => $item) {
            if ($item['product_id'] === $productId) {
                $this->items[$index]['quantity']++;
                $this->items[$index]['total'] = $this->items[$index]['quantity'] * $this->items[$index]['cost_price'];
                $this->showProductModal = false;
                return;
            }
        }

        // Get historical cost if available, else use product default
        $lastPurchaseItem = PurchaseOrderItem::where('product_id', $product->id)
            ->latest('id')
            ->first();
            
        $costPrice = $lastPurchaseItem ? (float)$lastPurchaseItem->cost_price : (float)$product->cost_price;

        $this->items[] = [
            'id' => null,
            'product_id' => $product->id,
            'product_name' => $product->localized_name,
            'sku' => $product->sku,
            'quantity' => 1,
            'received_qty' => 0,
            'cost_price' => $costPrice,
            'total' => $costPrice,
            'batch_no' => '',
            'expiry_date' => '',
        ];

        $this->showProductModal = false;
    }

    public function removeItem(int $index)
    {
        unset($this->items[$index]);
        $this->items = array_values($this->items);
    }

    public function updatedItems($value, $key)
    {
        if (str_contains($key, '.')) {
            $parts = explode('.', $key);
            $index = $parts[0];
            $property = $parts[1];
            
            if (in_array($property, ['quantity', 'cost_price'])) {
                $this->updateItemTotal((int)$index);
            }
        }
    }

    public function updateItemTotal(int $index)
    {
        if (isset($this->items[$index])) {
            $this->items[$index]['total'] = floatval($this->items[$index]['quantity']) * floatval($this->items[$index]['cost_price']);
        }
    }

    public function getSubtotalProperty()
    {
        return array_sum(array_column($this->items, 'total'));
    }

    #[Computed]
    public function products()
    {
        return Product::query()
            ->active()
            ->physical()
            ->when($this->productSearch, function ($query) {
                $query->where(function ($q) {
                    $q->where('name', 'like', '%' . $this->productSearch . '%')
                      ->orWhere('name_ar', 'like', '%' . $this->productSearch . '%')
                      ->orWhere('sku', 'like', '%' . $this->productSearch . '%')
                      ->orWhere('barcode', 'like', '%' . $this->productSearch . '%');
                });
            })
            ->take(20)
            ->get();
    }

    public function save()
    {
        $this->validate();

        if (empty($this->items)) {
            session()->flash('error', __('Please add at least one product.'));
            return;
        }

        try {
            $data = [
                'partner_id' => $this->partner_id,
                'location_id' => $this->locationId,
                'order_date' => $this->order_date,
                'expected_date' => $this->expected_date ?: null,
                'notes' => $this->notes ?: null,
                'subtotal' => $this->subtotal,
                'total' => $this->subtotal,
            ];

            if ($this->orderId) {
                // Update existing
                $this->order->update($data);
                $order = $this->order;

                // Sync items
                $existingIds = [];
                foreach ($this->items as $item) {
                    if ($item['id']) {
                        $existingIds[] = $item['id'];
                        PurchaseOrderItem::where('id', $item['id'])->update([
                            'quantity' => $item['quantity'],
                            'cost_price' => $item['cost_price'],
                            'total' => $item['total'],
                            'batch_no' => $item['batch_no'] ?: null,
                            'expiry_date' => $item['expiry_date'] ?: null,
                        ]);
                    } else {
                        $newItem = $order->items()->create([
                            'product_id' => $item['product_id'],
                            'quantity' => $item['quantity'],
                            'cost_price' => $item['cost_price'],
                            'total' => $item['total'],
                        ]);
                        $existingIds[] = $newItem->id;
                    }
                }

                // Delete removed items
                $order->items()->whereNotIn('id', $existingIds)->delete();

                session()->flash('success', __('Purchase order updated successfully.'));
            } else {
                // Create new
                $data['po_number'] = $this->generatePoNumber();
                $data['user_id'] = auth()->id();
                $data['branch_id'] = auth()->user()->branch_id ?? 1;
                $data['status'] = 'draft';

                $order = PurchaseOrder::create($data);

                foreach ($this->items as $item) {
                    $newItem = $order->items()->create([
                        'product_id' => $item['product_id'],
                        'quantity' => $item['quantity'],
                        'cost_price' => $item['cost_price'],
                        'total' => $item['total'],
                        'batch_no' => $item['batch_no'] ?: null,
                        'expiry_date' => $item['expiry_date'] ?: null,
                    ]);
                }

                session()->flash('success', __('Purchase order created successfully.'));
            }

            return redirect()->route('purchases.index');

        } catch (\Exception $e) {
            session()->flash('error', __('Error saving purchase order: ') . $e->getMessage());
        }
    }

    protected function generatePoNumber(): string
    {
        $prefix = Setting::get('po_prefix', 'PO');
        
        // maximize robustness: get the highest number from ALL records (including deleted)
        // Extract the number part using raw SQL or regex if possible, but for now simple string parsing
        // We look for the last created record to get a baseline
        $lastPo = PurchaseOrder::withTrashed()->orderBy('id', 'desc')->first();
        
        $nextNumber = 1;
        if ($lastPo) {
             // Extract number from "PO000001" -> 1
             $numberPart = substr($lastPo->po_number, strlen($prefix));
             if (is_numeric($numberPart)) {
                 $nextNumber = intval($numberPart) + 1;
             }
        }

        // Ensure uniqueness loop
        do {
            $poNumber = $prefix . str_pad($nextNumber, 6, '0', STR_PAD_LEFT);
            $exists = PurchaseOrder::withTrashed()->where('po_number', $poNumber)->exists();
            if ($exists) {
                $nextNumber++;
            }
        } while ($exists);

        return $poNumber;
    }

    public function submitOrder()
    {
        if ($this->order && $this->order->status === 'draft') {
            $this->order->update(['status' => 'ordered']);
            $this->status = 'ordered';
            session()->flash('success', __('Purchase order submitted successfully.'));
        }
    }

    public function render()
    {
        return view('livewire.purchase-orders.form');
    }
}
