<?php
namespace App\Http\Controllers;
use App\Models as M;
use App\Services\SequenceService;
use App\Services\StockService;
use App\Support\CrudRegistry;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class GoodsReceiptController extends Controller {
    use Concerns\FxDoc;
    public function index(Request $r) { return app(CrudController::class)->index($r, 'goods-receipts'); }
    public function show(int $id) { return app(CrudController::class)->show('goods-receipts', $id); }
    public function create(Request $r) {
        $this->need('purchase.create');
        $g = new M\GoodsReceipt(['grn_date' => date('Y-m-d')]);
        $po = $r->filled('from_po') ? M\PurchaseOrder::with('lines')->find($r->from_po) : null;
        $prefill = [];
        if ($po) {
            $g->purchase_order_id = $po->id; $g->supplier_id = $po->supplier_id; $this->fxFill($g, $po);
            foreach ($po->lines as $l) {
                $bal = $l->qty_ordered - $l->qty_received;
                if ($bal > 0) $prefill[] = ['item_id' => $l->item_id, 'product_id' => $l->product_id, 'qty_ordered' => $bal, 'qty_received' => $bal, 'qty_accepted' => $bal, 'qty_rejected' => 0, 'rate' => $l->rate];
            }
        }
        $data = $this->formData($g);
        $data['prefill'] = $prefill;
        return view('docs.goods-receipt.form', $data);
    }
    public function store(Request $r) {
        $this->need('purchase.create');
        $d = $this->validated($r);
        $g = DB::transaction(function () use ($d) {
            $g = M\GoodsReceipt::create($d['head'] + ['grn_no' => SequenceService::next('goods_receipt'), 'status' => 'draft', 'received_by' => Auth::user()->name]);
            foreach ($d['lines'] as $l) $g->lines()->create($l + ['amount' => round($l['qty_accepted'] * $l['rate'], 2)]);
            return $g;
        });
        return redirect()->route('goods-receipts.show', $g)->with('success', 'GRN '.$g->grn_no.' drafted — post it to update stock.');
    }
    public function destroy(int $id) {
        $this->need('purchase.delete');
        $g = M\GoodsReceipt::findOrFail($id);
        abort_unless($g->status === 'draft', 403, 'Only drafts can be deleted.');
        $g->delete();
        return redirect()->route('goods-receipts.index')->with('success', 'GRN deleted.');
    }
    public function post(int $id) {
        $this->need('purchase.approve');
        $g = M\GoodsReceipt::with('lines')->findOrFail($id);
        abort_unless($g->status === 'draft', 403, 'Already posted.');
        DB::transaction(function () use ($g) {
            foreach ($g->lines as $l) {
                if ($l->qty_accepted <= 0) continue;
                if ($l->item_id) {
                    StockService::postLedger($g->grn_date->format('Y-m-d'), $l->item_id, $g->warehouse_id, 'purchase', $l->qty_accepted, 0, $l->rate, 'goods_receipt', $g->id, [], 'GRN '.$g->grn_no);
                } elseif ($l->product_id) {
                    $po = $g->purchase_order_id ? M\PurchaseOrder::find($g->purchase_order_id) : null;
                    if ($po?->quarry_id) {
                        $pile = StockService::stockpileFor($po->quarry_id, $l->product_id);
                        StockService::moveStockpile($pile->id, $g->grn_date->format('Y-m-d'), 'production-in', $l->qty_accepted, 0, 'goods_receipt', $g->id, 'GRN '.$g->grn_no);
                    }
                }
                if ($g->purchase_order_id) {
                    $ol = M\PurchaseOrderLine::where('purchase_order_id', $g->purchase_order_id)
                        ->where(fn($q) => $q->where('item_id', $l->item_id)->orWhere('product_id', $l->product_id))->first();
                    if ($ol) $ol->increment('qty_received', $l->qty_accepted);
                }
            }
            $g->update(['status' => 'posted']);
            if ($g->purchase_order_id) PurchaseOrderController::refreshReceiveStatus(M\PurchaseOrder::find($g->purchase_order_id));
        });
        return back()->with('success', 'GRN posted — stock updated.');
    }
    public function bill(int $id) {
        $this->need('purchase.create');
        $g = M\GoodsReceipt::findOrFail($id);
        abort_unless($g->status === 'posted', 403, 'Only posted GRNs can be billed.');
        return redirect()->route('purchase-invoices.create', ['from_grn' => $g->id]);
    }
    public function print(Request $r, int $id) {
        $this->need('purchase.view');
        $doc = M\GoodsReceipt::with('supplier', 'warehouse', 'lines.item', 'lines.product')->findOrFail($id);
        if ($r->get('pdf')) return \App\Services\PdfService::download('docs.goods-receipt.print', ['g' => $doc], 'GRN-'.($doc->grn_no ?? $id).'.pdf');
        return view('docs.goods-receipt.print', ['g' => $doc]);
    }
    protected function validated(Request $r): array {
        $head = $r->validate(['supplier_id' => 'required|exists:suppliers,id', 'grn_date' => 'required|date', 'purchase_order_id' => 'nullable|exists:purchase_orders,id',
            'warehouse_id' => 'required|exists:warehouses,id', 'weighbridge_ticket_id' => 'nullable|exists:weighbridge_tickets,id', 'remarks' => 'nullable|max:500',
            'lines' => 'required|array|min:1', 'lines.*.item_id' => 'nullable|exists:stock_items,id', 'lines.*.product_id' => 'nullable|exists:products,id',
            'lines.*.qty_ordered' => 'nullable|numeric|min:0', 'lines.*.qty_received' => 'required|numeric|min:0', 'lines.*.qty_accepted' => 'required|numeric|min:0',
            'lines.*.qty_rejected' => 'nullable|numeric|min:0', 'lines.*.rate' => 'required|numeric|min:0', 'lines.*.batch_no' => 'nullable|max:60'] + $this->fxRules());
        $lines = $head['lines']; unset($head['lines']);
        return ['head' => $head, 'lines' => $lines];
    }
    protected function formData(M\GoodsReceipt $g): array {
        return ['fx' => \App\Services\CurrencyService::active(), 'record' => $g, 'isNew' => !$g->exists, 'cfg' => CrudRegistry::get('goods-receipts'), 'key' => 'goods-receipts',
            'docNo' => SequenceService::peek('goods_receipt'),
            'suppliers' => M\Supplier::where('is_active', true)->orderBy('name')->get(), 'warehouses' => M\Warehouse::orderBy('name')->get(),
            'items' => M\StockItem::where('is_active', true)->orderBy('name')->get(), 'products' => M\Product::where('is_active', true)->orderBy('name')->get(),
            'tickets' => M\WeighbridgeTicket::where('status', 'closed')->whereNull('delivery_note_id')->orderByDesc('id')->limit(200)->get()];
    }
}
