<?php

namespace App\Services;

use App\Models\Lookups\Status;
use App\Models\Purchasing\PurchaseOrder;
use App\Models\Purchasing\PurchaseOrderItem;
use App\Support\Quantity;
use App\Support\WhatsApp;
use Illuminate\Support\Facades\DB;
use RuntimeException;

class PurchaseOrderService
{
    public function __construct(private readonly DocumentNumberService $numbers) {}

    /**
     * @param  array{supplier_id:int, branch_id:int, expected_date?:string, notes?:string}  $header
     * @param  array<int, array{product_id:int,  unit_id?:int, quantity:float, unit_cost?:float}>  $lines
     */
    public function create(array $header, array $lines): PurchaseOrder
    {
        return DB::transaction(function () use ($header, $lines) {
            $po = PurchaseOrder::create([
                'po_number' => $this->numbers->next('purchase_order'),
                'supplier_id' => $header['supplier_id'],
                'branch_id' => $header['branch_id'],
                'status_id' => Status::idOf(Status::CTX_PURCHASE_ORDER, Status::PO_DRAFT),
                'expected_date' => $header['expected_date'] ?? null,
                'notes' => $header['notes'] ?? null,
            ]);

            foreach ($lines as $line) {
                PurchaseOrderItem::create([
                    'purchase_order_id' => $po->id,
                    'product_id' => $line['product_id'],
                    'unit_id' => $line['unit_id'] ?? null,
                    'ordered_quantity' => $line['quantity'],
                    'unit_cost' => $line['unit_cost'] ?? null,
                ]);
            }

            AuditLog::record('purchase_order.created', $po, [
                'po_number' => $po->po_number,
                'supplier_id' => $po->supplier_id,
                'items_count' => count($lines),
            ]);

            return $po;
        });
    }

    public function send(PurchaseOrder $po): void
    {
        if (! $po->isDraft()) {
            throw new RuntimeException('Only draft purchase orders can be sent.');
        }

        $po->update(['status_id' => Status::idOf(Status::CTX_PURCHASE_ORDER, Status::PO_SENT)]);
        AuditLog::record('purchase_order.sent', $po, ['po_number' => $po->po_number]);
    }

    /**
     * The pre-filled WhatsApp message for a PO — header, every line and the
     * running total — ready to paste into a wa.me deep link. No HTML, no
     * markdown beyond the *bold* WhatsApp itself renders.
     */
    public function whatsappMessage(PurchaseOrder $po): string
    {
        $po->loadMissing(['branch', 'items.product', 'items.unit']);

        $symbol = app(CurrentContext::class)->currencySymbol();
        $money = fn (float $amount): string => $symbol.number_format($amount, 2);

        $out = "*PURCHASE ORDER {$po->po_number}*\n";

        if ($po->branch !== null) {
            $out .= "Branch: {$po->branch->name}\n";
        }
        if ($po->expected_date !== null) {
            $out .= 'Expected date: '.$po->expected_date->format('Y-m-d')."\n";
        }
        if (filled($po->notes)) {
            $out .= "Notes: {$po->notes}\n";
        }

        $out .= "\nItems:\n";

        $total = 0.0;
        foreach ($po->items as $index => $item) {
            $cost = $item->unit_cost !== null ? (float) $item->unit_cost : 0.0;
            $lineTotal = $cost * (float) $item->ordered_quantity;
            $total += $lineTotal;

            $out .= sprintf(
                "%d. %s — %s × %s = %s\n",
                $index + 1,
                $item->product?->name ?? 'Unknown product',
                Quantity::format($item->ordered_quantity, $item->unit),
                $money($cost),
                $money($lineTotal),
            );
        }

        $out .= "\nTotal: {$money($total)}\n";
        $out .= "\nPlease confirm this order. Thank you!";

        return $out;
    }

    /**
     * The wa.me deep link for this PO, or null when the supplier has no
     * phone number on file — callers hide the send option in that case.
     */
    public function whatsappLink(PurchaseOrder $po): ?string
    {
        $phone = $po->supplier?->phone;

        if (blank($phone)) {
            return null;
        }

        return WhatsApp::link($phone, $this->whatsappMessage($po));
    }

    public function cancel(PurchaseOrder $po, string $reason): void
    {
        if ($po->status?->code === Status::PO_RECEIVED) {
            throw new RuntimeException('A fully received purchase order cannot be cancelled.');
        }

        $po->update([
            'status_id' => Status::idOf(Status::CTX_PURCHASE_ORDER, Status::PO_CANCELLED),
            'notes' => trim(($po->notes ?? '')."\n[CANCELLED] {$reason}"),
        ]);
        AuditLog::record('purchase_order.cancelled', $po, ['reason' => $reason]);
    }

    /**
     * Called by GrnService::confirm() when a GRN references this PO. Updates
     * each line's received_quantity and flips status to partial/received.
     *
     * @param  array<int,float>  $receivedQtyByProductId  base-unit quantity received per product_id
     */
    public function applyGrnReceipt(PurchaseOrder $po, array $receivedQtyByProductId): void
    {
        foreach ($po->items as $item) {
            $received = $receivedQtyByProductId[$item->product_id] ?? 0.0;
            if ($received > 0) {
                $item->increment('received_quantity', $received);
            }
        }

        $po->refresh()->load('items');
        $newStatus = $po->isFullyReceived() ? Status::PO_RECEIVED : Status::PO_PARTIAL;
        $po->update(['status_id' => Status::idOf(Status::CTX_PURCHASE_ORDER, $newStatus)]);

        AuditLog::record('purchase_order.receipt_applied', $po, ['status' => $newStatus]);
    }
}
