<?php
namespace App\Http\Controllers;
use App\Models as M;
use App\Services\ApprovalService;
use App\Services\GhanaTaxService;
use App\Services\SequenceService;
use App\Support\CrudRegistry;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
class PurchaseOrderController extends Controller {
    use Concerns\FxDoc;
    public function index(Request $r) { return app(CrudController::class)->index($r, 'purchase-orders'); }
    public function show(int $id) { return app(CrudController::class)->show('purchase-orders', $id); }
    public function create(Request $r) {
        $this->need('purchase.create');
        $po = new M\PurchaseOrder(['po_date' => date('Y-m-d')]);
        $fromPr = $r->filled('from_pr') ? M\PurchaseRequisition::with('lines')->find($r->from_pr) : null;
        $prefill = [];
        if ($fromPr) {
            $po->requisition_id = $fromPr->id; $po->quarry_id = $fromPr->quarry_id; $po->project_id = $fromPr->project_id;
            foreach ($fromPr->lines as $l) $prefill[] = ['item_id' => $l->item_id, 'description' => $l->description, 'qty_ordered' => $l->qty, 'unit_id' => $l->unit_id, 'rate' => $l->estimated_rate];
        }
        $data = $this->formData($po);
        $data['prefill'] = $prefill;
        return view('docs.purchase-order.form', $data);
    }
    public function store(Request $r) {
        $this->need('purchase.create');
        $d = $this->validated($r);
        $po = DB::transaction(function () use ($d) {
            $po = M\PurchaseOrder::create($d['head'] + ['po_no' => SequenceService::next('purchase_order'), 'status' => 'draft', 'prepared_by' => Auth::id()]);
            $this->saveLines($po, $d['lines']);
            return $po;
        });
        return redirect()->route('purchase-orders.show', $po)->with('success', 'LPO '.$po->po_no.' drafted.');
    }
    public function edit(int $id) {
        $this->need('purchase.edit');
        $po = M\PurchaseOrder::with('lines')->findOrFail($id);
        abort_unless(in_array($po->status, ['draft']), 403, 'Only drafts can be edited.');
        $data = $this->formData($po);
        $data['prefill'] = [];
        return view('docs.purchase-order.form', $data);
    }
    public function update(Request $r, int $id) {
        $this->need('purchase.edit');
        $po = M\PurchaseOrder::findOrFail($id);
        abort_unless($po->status === 'draft', 403, 'Only drafts can be edited.');
        $d = $this->validated($r);
        DB::transaction(function () use ($po, $d) {
            $po->update($d['head']);
            $po->lines()->delete();
            $this->saveLines($po, $d['lines']);
        });
        return redirect()->route('purchase-orders.show', $po)->with('success', 'LPO updated.');
    }
    public function destroy(int $id) {
        $this->need('purchase.delete');
        $po = M\PurchaseOrder::findOrFail($id);
        abort_unless($po->status === 'draft', 403, 'Only drafts can be deleted.');
        $po->delete();
        return redirect()->route('purchase-orders.index')->with('success', 'LPO deleted.');
    }
    public function submit(int $id) {
        $this->need('purchase.edit');
        $po = M\PurchaseOrder::findOrFail($id);
        abort_unless($po->status === 'draft', 403, 'Only drafts can be submitted.');
        $po->update(['status' => 'submitted'] ?? ['status' => 'submitted']);
        M\PurchaseOrder::where('id', $po->id)->update(['status' => 'submitted']);
        ApprovalService::request('purchase_order', $po->id, $po->po_no);
        return back()->with('success', 'LPO submitted for approval.');
    }
    public function receive(int $id) {
        $this->need('purchase.create');
        $po = M\PurchaseOrder::findOrFail($id);
        abort_unless(in_array($po->status, ['sent', 'partially-received']), 403, 'Only approved LPOs can be received.');
        return redirect()->route('goods-receipts.create', ['from_po' => $po->id]);
    }
    public function print(Request $r, int $id) {
        $this->need('purchase.view');
        $doc = M\PurchaseOrder::with('supplier', 'lines.item', 'lines.unit', 'quarry')->findOrFail($id);
        if ($r->get('pdf')) return \App\Services\PdfService::download('docs.purchase-order.print', ['p' => $doc], 'LPO-'.($doc->po_no ?? $id).'.pdf');
        return view('docs.purchase-order.print', ['p' => $doc]);
    }
    protected function validated(Request $r): array {
        $head = $r->validate(['supplier_id' => 'required|exists:suppliers,id', 'po_date' => 'required|date', 'requisition_id' => 'nullable|exists:purchase_requisitions,id',
            'quarry_id' => 'nullable|exists:quarries,id', 'project_id' => 'nullable|exists:projects,id', 'delivery_address' => 'nullable|max:255',
            'expected_date' => 'nullable|date', 'withholding_tax' => 'nullable|numeric|min:0', 'terms' => 'nullable|max:2000',
            'lines' => 'required|array|min:1', 'lines.*.item_id' => 'nullable|exists:stock_items,id', 'lines.*.product_id' => 'nullable|exists:products,id',
            'lines.*.description' => 'required|max:255', 'lines.*.qty_ordered' => 'required|numeric|min:0.001', 'lines.*.unit_id' => 'nullable|exists:units,id', 'lines.*.rate' => 'required|numeric|min:0'] + $this->fxRules());
        $lines = $head['lines']; unset($head['lines']);
        return ['head' => $head, 'lines' => $lines];
    }
    protected function saveLines(M\PurchaseOrder $po, array $lines): void {
        $sub = 0;
        foreach ($lines as $l) {
            $amt = round($l['qty_ordered'] * $l['rate'], 2);
            $po->lines()->create(['item_id' => $l['item_id'] ?? null, 'product_id' => $l['product_id'] ?? null, 'description' => $l['description'],
                'qty_ordered' => $l['qty_ordered'], 'qty_received' => 0, 'unit_id' => $l['unit_id'] ?? null, 'rate' => $l['rate'], 'amount' => $amt]);
            $sub += $amt;
        }
        $tax = GhanaTaxService::salesTax($sub);
        $wht = (float) ($po->withholding_tax ?? 0);
        $po->update(['subtotal' => round($sub, 2), 'tax_amount' => $tax['total_tax'], 'grand_total' => round($tax['grand'] - $wht, 2)]);
        if ($po->requisition_id) M\PurchaseRequisition::where('id', $po->requisition_id)->update(['status' => 'converted']);
    }
    public static function refreshReceiveStatus(M\PurchaseOrder $po): void {
        $ord = (float) $po->lines()->sum('qty_ordered');
        $rec = (float) $po->lines()->sum('qty_received');
        $po->update(['status' => $rec <= 0 ? 'sent' : ($rec < $ord ? 'partially-received' : 'received')]);
    }
    protected function formData(M\PurchaseOrder $po): array {
        return ['fx' => \App\Services\CurrencyService::active(), 'record' => $po, 'isNew' => !$po->exists, 'cfg' => CrudRegistry::get('purchase-orders'), 'key' => 'purchase-orders',
            'docNo' => $po->exists ? $po->po_no : SequenceService::peek('purchase_order'),
            'suppliers' => M\Supplier::where('is_active', true)->orderBy('name')->get(), 'quarries' => M\Quarry::orderBy('name')->get(),
            'projects' => M\Project::orderBy('code')->get(), 'items' => M\StockItem::where('is_active', true)->orderBy('name')->get(),
            'products' => M\Product::where('is_active', true)->orderBy('name')->get(), 'units' => M\Unit::orderBy('name')->get()];
    }
}
