<?php

namespace Modules\Inventory\Controllers;

use core\Controller;
use core\Auth;
use core\Database;

class PurchaseController extends Controller
{
    public function index(): void
    {
        $this->requirePermission('purchases_view');

        $search = $this->request->get('search', '');
        $status = $this->request->get('status', '');
        $supplierId = $this->request->get('supplier_id', '');
        $dateFrom = $this->request->get('date_from', '');
        $dateTo = $this->request->get('date_to', '');
        $page = max(1, (int)$this->request->get('page', 1));
        $perPage = PAGINATION_LIMIT;
        $db = Database::getInstance();

        $query = $db->table('purchase_orders po')
            ->select('po.*, s.name as supplier_name,
                     u.first_name as user_first_name, u.last_name as user_last_name')
            ->leftJoin('suppliers s', 'po.supplier_id', '=', 's.id')
            ->leftJoin('users u', 'po.created_by', '=', 'u.id');

        if (!empty($search)) {
            $query->where(function ($q) use ($search) {
                $q->where('po.order_number', 'LIKE', '%' . $search . '%')
                  ->orWhere('s.name', 'LIKE', '%' . $search . '%');
            });
        }

        if (!empty($status)) {
            $query->where('po.status', $status);
        }

        if (!empty($supplierId)) {
            $query->where('po.supplier_id', $supplierId);
        }

        if (!empty($dateFrom)) {
            $query->where('po.order_date', '>=', $dateFrom);
        }

        if (!empty($dateTo)) {
            $query->where('po.order_date', '<=', $dateTo);
        }

        $orders = $query->orderBy('po.created_at', 'DESC')
            ->paginate($perPage, $page);

        $suppliers = $db->table('suppliers')
            ->where('is_active', 1)
            ->orderBy('name', 'ASC')
            ->get();

        $this->view('Inventory/purchases/index', [
            'title' => 'Purchase Orders',
            'breadcrumb' => ['Purchases' => url('purchases')],
            'orders' => $orders,
            'search' => $search,
            'status' => $status,
            'supplierId' => $supplierId,
            'dateFrom' => $dateFrom,
            'dateTo' => $dateTo,
            'suppliers' => $suppliers,
        ]);
    }

    public function create(): void
    {
        $this->requirePermission('purchases_create');

        $db = Database::getInstance();
        $suppliers = $db->table('suppliers')
            ->where('deleted_at', 'IS', null)
            ->where('is_active', 1)
            ->orderBy('name', 'ASC')
            ->get();

        $products = $db->table('products')
            ->where('deleted_at', 'IS', null)
            ->where('is_active', 1)
            ->orderBy('name', 'ASC')
            ->get();

        $this->view('Inventory/purchases/create', [
            'title' => 'Create Purchase Order',
            'breadcrumb' => [
                'Purchases' => url('purchases'),
                'Create' => url('purchases/create'),
            ],
            'suppliers' => $suppliers,
            'products' => $products,
        ]);
    }

    public function store(): void
    {
        $this->requirePermission('purchases_create');

        $validated = $this->validate([
            'supplier_id' => 'required|integer',
            'expected_date' => 'date',
            'notes' => 'max_length:2000',
            'items' => 'required',
        ]);

        $items = $this->request->post('items', []);
        if (empty($items) || !is_array($items)) {
            $this->redirectWith(url('purchases/create'), 'error', 'At least one item is required.');
        }

        $db = Database::getInstance();
        $db->beginTransaction();

        try {
            $orderNumber = generatePoNumber();
            $totalAmount = 0;

            foreach ($items as $item) {
                if (empty($item['product_id']) || empty($item['quantity']) || empty($item['unit_cost'])) {
                    continue;
                }
                $totalAmount += (float)$item['quantity'] * (float)$item['unit_cost'];
            }

            $orderId = $db->table('purchase_orders')->insert([
                'order_number' => $orderNumber,
                'supplier_id' => (int)$validated['supplier_id'],
                'order_date' => date('Y-m-d'),
                'expected_date' => $validated['expected_date'] ?? null,
                'status' => 'draft',
                'total_amount' => $totalAmount,
                'notes' => $validated['notes'] ?? '',
                'created_by' => Auth::id(),
                'created_at' => date(DATE_TIME_FORMAT),
                'updated_at' => date(DATE_TIME_FORMAT),
            ]);

            foreach ($items as $item) {
                if (empty($item['product_id']) || empty($item['quantity']) || empty($item['unit_cost'])) {
                    continue;
                }
                $db->table('purchase_order_items')->insert([
                    'purchase_order_id' => $orderId,
                    'product_id' => (int)$item['product_id'],
                    'quantity' => (int)$item['quantity'],
                    'unit_cost' => (float)$item['unit_cost'],
                    'total_cost' => (int)$item['quantity'] * (float)$item['unit_cost'],
                    'created_at' => date(DATE_TIME_FORMAT),
                ]);
            }

            $db->commit();
            $this->logActivity('create', 'Created purchase order: ' . $orderNumber);
            $this->logAudit('create', 'purchase_orders', $orderId, null, ['order_number' => $orderNumber, 'supplier_id' => $validated['supplier_id']]);

            $this->redirectWith(url('purchases/show/' . $orderId), 'success', 'Purchase order created successfully.');
        } catch (\Throwable $e) {
            $db->rollback();
            $this->redirectWith(url('purchases/create'), 'error', 'Failed to create purchase order: ' . $e->getMessage());
        }
    }

    public function show(int $id): void
    {
        $this->requirePermission('purchases_view');

        $db = Database::getInstance();
        $order = $db->table('purchase_orders po')
            ->select('po.*, s.name as supplier_name, s.contact_person, s.email as supplier_email,
                     s.phone as supplier_phone, s.address as supplier_address,
                     u.first_name as user_first_name, u.last_name as user_last_name')
            ->leftJoin('suppliers s', 'po.supplier_id', '=', 's.id')
            ->leftJoin('users u', 'po.created_by', '=', 'u.id')
            ->where('po.id', $id)
            ->where('po.deleted_at', 'IS', null)
            ->first();

        if (!$order) {
            $this->redirectWith(url('purchases'), 'error', 'Purchase order not found.');
        }

        $items = $db->table('purchase_order_items pi')
            ->select('pi.*, p.name as product_name, p.sku, p.unit_type')
            ->leftJoin('products p', 'pi.product_id', '=', 'p.id')
            ->where('pi.purchase_order_id', $id)
            ->get();

        $grn = $db->table('goods_received_notes')
            ->where('purchase_order_id', $id)
            ->first();

        $this->view('Inventory/purchases/show', [
            'title' => 'Purchase Order - ' . $order->order_number,
            'breadcrumb' => [
                'Purchases' => url('purchases'),
                $order->order_number => url('purchases/show/' . $id),
            ],
            'order' => $order,
            'items' => $items,
            'grn' => $grn,
        ]);
    }

    public function approve(int $id): void
    {
        $this->requirePermission('purchases_approve');

        $db = Database::getInstance();
        $order = $db->table('purchase_orders')
            ->where('id', $id)
            ->where('deleted_at', 'IS', null)
            ->first();

        if (!$order) {
            $this->redirectWith(url('purchases'), 'error', 'Purchase order not found.');
        }

        if ($order->status !== 'draft') {
            $this->redirectWith(url('purchases/show/' . $id), 'error', 'Only draft orders can be approved.');
        }

        $db->table('purchase_orders')
            ->where('id', $id)
            ->update([
                'status' => 'approved',
                'approved_by' => Auth::id(),
                'approved_at' => date(DATE_TIME_FORMAT),
                'updated_at' => date(DATE_TIME_FORMAT),
            ]);

        $this->logActivity('approve', 'Approved purchase order: ' . $order->order_number);
        $this->logAudit('approve', 'purchase_orders', $id, ['status' => 'draft'], ['status' => 'approved']);

        $this->redirectWith(url('purchases/show/' . $id), 'success', 'Purchase order approved successfully.');
    }

    public function receive(int $id): void
    {
        $this->requirePermission('purchases_receive');

        $db = Database::getInstance();
        $order = $db->table('purchase_orders')
            ->where('id', $id)
            ->where('deleted_at', 'IS', null)
            ->first();

        if (!$order) {
            $this->redirectWith(url('purchases'), 'error', 'Purchase order not found.');
        }

        if ($order->status !== 'approved') {
            $this->redirectWith(url('purchases/show/' . $id), 'error', 'Only approved orders can be received.');
        }

        $items = $db->table('purchase_order_items pi')
            ->select('pi.*, p.name as product_name')
            ->leftJoin('products p', 'pi.product_id', '=', 'p.id')
            ->where('pi.purchase_order_id', $id)
            ->get();

        if (empty($items)) {
            $this->redirectWith(url('purchases/show/' . $id), 'error', 'No items to receive.');
        }

        $db->beginTransaction();

        try {
            $postItems = $this->request->post('items', []);

            foreach ($items as $item) {
                $itemData = $postItems[$item->product_id] ?? null;
                if ($itemData) {
                    $hasBatch = !empty($itemData['batch_number']);
                    $hasExpiry = !empty($itemData['expiry_date']);
                    if ($hasBatch && !$hasExpiry) {
                        throw new \RuntimeException('Expiry date is required when batch number is provided for "' . e($item->product_name ?? 'product') . '".');
                    }
                    if (!$hasBatch && $hasExpiry) {
                        throw new \RuntimeException('Batch number is required when expiry date is provided for "' . e($item->product_name ?? 'product') . '".');
                    }
                }
            }

            $grnNumber = generateGrnNumber();

            $grnId = $db->table('goods_received_notes')->insert([
                'grn_number' => $grnNumber,
                'purchase_order_id' => $id,
                'received_date' => date('Y-m-d'),
                'received_by' => Auth::id(),
                'notes' => 'Goods received for PO: ' . $order->order_number,
                'created_at' => date(DATE_TIME_FORMAT),
            ]);

            foreach ($items as $item) {
                $db->table('grn_items')->insert([
                    'grn_id' => $grnId,
                    'product_id' => $item->product_id,
                    'quantity_ordered' => $item->quantity,
                    'quantity_received' => $item->quantity,
                    'quantity_accepted' => $item->quantity,
                    'quantity_rejected' => 0,
                    'unit_cost' => $item->unit_cost,
                    'created_at' => date(DATE_TIME_FORMAT),
                ]);

                $itemData = $postItems[$item->product_id] ?? null;
                if ($itemData && !empty($itemData['batch_number'])) {
                    $existing = $db->table('product_batches')
                        ->where('product_id', (int)$item->product_id)
                        ->where('batch_number', $itemData['batch_number'])
                        ->first();

                    if (!$existing) {
                        $db->table('product_batches')->insert([
                            'product_id' => (int)$item->product_id,
                            'batch_number' => $itemData['batch_number'],
                            'expiry_date' => $itemData['expiry_date'],
                            'quantity' => (int)$item->quantity,
                            'cost_price' => (float)$item->unit_cost,
                            'received_date' => date('Y-m-d'),
                        ]);
                    }
                }
            }

            $db->table('purchase_orders')
                ->where('id', $id)
                ->update([
                    'status' => 'received',
                    'updated_at' => date(DATE_TIME_FORMAT),
                ]);

            $db->commit();

            $this->logActivity('receive', 'Received goods for PO: ' . $order->order_number . ' (GRN: ' . $grnNumber . ')');
            $this->logAudit('receive', 'purchase_orders', $id, ['status' => 'approved'], ['status' => 'received']);

            $this->redirectWith(url('purchases/grn/' . $grnId), 'success', 'Goods received successfully. GRN: ' . $grnNumber);
        } catch (\Throwable $e) {
            $db->rollback();
            $this->redirectWith(url('purchases/show/' . $id), 'error', 'Failed to receive goods: ' . $e->getMessage());
        }
    }

    public function grnList(): void
    {
        $this->requirePermission('purchases_view');

        $search = $this->request->get('search', '');
        $page = max(1, (int)$this->request->get('page', 1));
        $perPage = PAGINATION_LIMIT;
        $db = Database::getInstance();

        $query = $db->table('goods_received_notes grn')
            ->select('grn.*, po.order_number, po.supplier_id, s.name as supplier_name,
                     u.first_name as user_first_name, u.last_name as user_last_name')
            ->leftJoin('purchase_orders po', 'grn.purchase_order_id', '=', 'po.id')
            ->leftJoin('suppliers s', 'po.supplier_id', '=', 's.id')
            ->leftJoin('users u', 'grn.received_by', '=', 'u.id');

        if (!empty($search)) {
            $query->where(function ($q) use ($search) {
                $q->where('grn.grn_number', 'LIKE', '%' . $search . '%')
                  ->orWhere('po.order_number', 'LIKE', '%' . $search . '%')
                  ->orWhere('s.name', 'LIKE', '%' . $search . '%');
            });
        }

        $grns = $query->orderBy('grn.created_at', 'DESC')
            ->paginate($perPage, $page);

        $this->view('Inventory/purchases/index', [
            'title' => 'Goods Received Notes',
            'breadcrumb' => [
                'Purchases' => url('purchases'),
                'GRN' => url('purchases/grn'),
            ],
            'orders' => $grns,
            'search' => $search,
            'isGrn' => true,
        ]);
    }

    public function grnView(int $id): void
    {
        $this->requirePermission('purchases_view');

        $db = Database::getInstance();
        $grn = $db->table('goods_received_notes grn')
            ->select('grn.*, po.order_number, po.supplier_id, po.status,
                     s.name as supplier_name, s.contact_person, s.email as supplier_email, s.phone as supplier_phone,
                     u.first_name as user_first_name, u.last_name as user_last_name')
            ->leftJoin('purchase_orders po', 'grn.purchase_order_id', '=', 'po.id')
            ->leftJoin('suppliers s', 'po.supplier_id', '=', 's.id')
            ->leftJoin('users u', 'grn.received_by', '=', 'u.id')
            ->where('grn.id', $id)
            ->first();

        if (!$grn) {
            $this->redirectWith(url('purchases/grn'), 'error', 'GRN not found.');
        }

        $items = $db->table('grn_items gi')
            ->select('gi.*, p.name as product_name, p.sku, p.unit_type')
            ->leftJoin('products p', 'gi.product_id', '=', 'p.id')
            ->where('gi.grn_id', $id)
            ->get();

        $this->view('Inventory/purchases/show', [
            'title' => 'GRN - ' . $grn->grn_number,
            'breadcrumb' => [
                'Purchases' => url('purchases'),
                'GRN' => url('purchases/grn'),
                $grn->grn_number => url('purchases/grn/' . $id),
            ],
            'order' => $grn,
            'items' => $items,
            'isGrn' => true,
        ]);
    }
}
