<?php

namespace App\Controllers\View;

use App\Controllers\BaseController;
use App\Models\PurchaseOrderModel;
use App\Models\PurchaseOrderItemModel;
use App\Models\SupplierModel;
use App\Models\SupplyModel;
use App\Models\SupplyTransactionModel;

class PurchaseOrderController extends BaseController
{
    protected $poModel;
    protected $itemModel;
    protected $supplierModel;
    protected $supplyModel;
    protected $txnModel;
    protected $db;

    public function __construct()
    {
        $this->poModel       = new PurchaseOrderModel();
        $this->itemModel     = new PurchaseOrderItemModel();
        $this->supplierModel = new SupplierModel();
        $this->supplyModel   = new SupplyModel();
        $this->txnModel      = new SupplyTransactionModel();
        $this->db            = \Config\Database::connect();
    }

    public function index()
    {
        $q = trim($this->request->getGet('q') ?? '');
        $builder = $this->poModel->select('purchase_orders.*, suppliers.name AS supplier_name')
            ->join('suppliers', 'suppliers.id = purchase_orders.supplier_id', 'left');

        if ($q !== '') {
            $builder->groupStart()
                ->like('po_number', $q)
                ->orLike('suppliers.name', $q)
                ->groupEnd();
        }

        $orders = $builder->orderBy('purchase_orders.created_at', 'DESC')->paginate(20);
        return view('inventory/po_index', [
            'orders' => $orders,
            'pager'  => $this->poModel->pager,
            'q'      => $q,
        ]);
    }

    public function create()
    {
        return view('inventory/po_form', [
            'title'     => 'Create Purchase Order',
            'action'    => site_url('inventory/po/store'),
            'suppliers' => $this->supplierModel->orderBy('name')->findAll(),
            'supplies'  => $this->supplyModel->orderBy('name')->findAll(),
        ]);
    }

    public function store()
    {
        $po = $this->request->getPost([
            'po_number','supplier_id','order_date','expected_date','notes'
        ]);
        $status = $this->request->getPost('status') ?? 'ordered';

        $supply_ids  = $this->request->getPost('item_supply_id') ?? [];
        $descs       = $this->request->getPost('item_description') ?? [];
        $qtys        = $this->request->getPost('item_quantity') ?? [];
        $unit_costs  = $this->request->getPost('item_unit_cost') ?? [];

        if (empty($supply_ids)) {
            return redirect()->back()->withInput()->with('error', 'Add at least one line item.');
        }

        // compute totals
        $subtotal = 0.0;
        $items = [];
        foreach ($supply_ids as $i => $sid) {
            $q  = max(0, (int)($qtys[$i] ?? 0));
            $uc = (float)($unit_costs[$i] ?? 0);
            if ($sid && $q > 0) {
                $line = $q * $uc;
                $subtotal += $line;
                $items[] = [
                    'supply_id'   => (int)$sid,
                    'description' => trim($descs[$i] ?? ''),
                    'quantity'    => $q,
                    'unit_cost'   => $uc,
                    'received_qty'=> 0,
                ];
            }
        }
        if (!$items) {
            return redirect()->back()->withInput()->with('error', 'Valid line items required.');
        }
        $tax  = 0.00;
        $total = $subtotal + $tax;

        $this->db->transStart();

        $po['status'] = in_array($status, ['draft','ordered'], true) ? $status : 'ordered';
        $po['subtotal'] = $subtotal;
        $po['tax'] = $tax;
        $po['total'] = $total;

        if (!$this->poModel->save($po)) {
            $this->db->transRollback();
            return redirect()->back()->withInput()->with('error', implode("\n", $this->poModel->errors()));
        }
        $poId = $this->poModel->getInsertID();

        foreach ($items as $it) {
            $it['purchase_order_id'] = $poId;
            if (!$this->itemModel->insert($it)) {
                $this->db->transRollback();
                return redirect()->back()->withInput()->with('error', 'Failed to save line items.');
            }
        }

        $this->db->transComplete();
        if ($this->db->transStatus() === false) {
            return redirect()->back()->withInput()->with('error', 'Failed to save purchase order.');
        }

        return redirect()->to(site_url('inventory/po/show/'.$poId))->with('success', 'PO created.');
    }

    public function show($id)
    {
        $po = $this->poModel->select('purchase_orders.*, suppliers.name AS supplier_name')
              ->join('suppliers', 'suppliers.id = purchase_orders.supplier_id', 'left')
              ->find($id);
        if (!$po) return redirect()->to('inventory/po')->with('error', 'PO not found.');

        $items = $this->itemModel->select('purchase_order_items.*, supplies.name AS supply_name, supplies.unit as supply_unit')
                ->join('supplies', 'supplies.id = purchase_order_items.supply_id', 'left')
                ->where('purchase_order_id', $id)->findAll();

        return view('inventory/po_show', [
            'po' => $po,
            'items' => $items,
        ]);
    }

    /**
     * Receive items (partial or full)
     * POST body: received[item_id] = qty_to_receive
     */
    public function receive($id)
    {
        $po = $this->poModel->find($id);
        if (!$po || in_array($po['status'], ['canceled','received'], true)) {
            return redirect()->to('inventory/po/show/'.$id)->with('error', 'PO not receivable.');
        }

        $received = $this->request->getPost('received') ?? []; // [itemId => qty]
        if (!$received) {
            return redirect()->to('inventory/po/show/'.$id)->with('error', 'No items to receive.');
        }

        $issuedBy = (string) (session('user.email') ?? session('user.username') ?? 'system');

        $this->db->transStart();

        $completed = true;
        foreach ($received as $itemId => $qty) {
            $qty = (int)$qty;
            if ($qty <= 0) continue;

            $item = $this->itemModel->where('purchase_order_id', $id)->find($itemId);
            if (!$item) { $completed = false; continue; }

            $remaining = (int)$item['quantity'] - (int)$item['received_qty'];
            $toReceive = min($remaining, $qty);
            if ($toReceive <= 0) continue;

            // Update item received qty
            $this->itemModel->update($itemId, [
                'received_qty' => (int)$item['received_qty'] + $toReceive
            ]);

            // Update supply on hand
            $supply = $this->supplyModel->find($item['supply_id']);
            if (!$supply) { $completed = false; continue; }
            $newQty = (int)$supply['qty_on_hand'] + $toReceive;
            $this->supplyModel->update($supply['id'], ['qty_on_hand' => $newQty]);

            // Log transaction IN
            $this->txnModel->insert([
                'supply_id' => $supply['id'],
                'type'      => 'in',
                'quantity'  => $toReceive,
                'ref'       => 'PO ' . $po['po_number'],
                'issued_to' => 'Inventory',
                'issued_by' => $issuedBy,
                'notes'     => 'Received against PO',
            ]);

            if (($item['received_qty'] + $toReceive) < $item['quantity']) {
                $completed = false;
            }
        }

        // Set PO status
        $this->poModel->update($id, ['status' => $completed ? 'received' : 'ordered']);

        $this->db->transComplete();
        if ($this->db->transStatus() === false) {
            return redirect()->to('inventory/po/show/'.$id)->with('error', 'Failed to receive items.');
        }

        return redirect()->to('inventory/po/show/'.$id)->with('success', $completed ? 'PO fully received.' : 'PO partially received.');
    }

    public function cancel($id)
    {
        $po = $this->poModel->find($id);
        if (!$po || $po['status'] === 'received') {
            return redirect()->to('inventory/po/show/'.$id)->with('error', 'Cannot cancel this PO.');
        }
        $this->poModel->update($id, ['status' => 'canceled']);
        return redirect()->to('inventory/po/show/'.$id)->with('success', 'PO canceled.');
    }
}
