<?php

namespace App\Services;

use App\Models\Batch;
use App\Models\Product;
use App\Models\PurchaseOrder;
use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class PurchaseOrderService
{
    public function __construct(
        private readonly InventoryService $inventoryService,
        private readonly AuditLogService $auditLogService,
    ) {
    }

    public function create(User $user, int $companyId, int $branchId, array $data): PurchaseOrder
    {
        return DB::transaction(function () use ($user, $companyId, $branchId, $data) {
            $subtotal = 0;
            $taxAmount = 0;

            $purchaseOrder = PurchaseOrder::create([
                'company_id' => $companyId,
                'branch_id' => $branchId,
                'supplier_id' => $data['supplier_id'],
                'user_id' => $user->id,
                'reference_number' => $data['reference_number'] ?? $this->generateReference(),
                'status' => $data['status'] ?? 'draft',
                'order_date' => $data['order_date'],
                'expected_date' => $data['expected_date'] ?? null,
                'discount_amount' => $data['discount_amount'] ?? 0,
                'shipping_cost' => $data['shipping_cost'] ?? 0,
                'amount_paid' => $data['amount_paid'] ?? 0,
                'notes' => $data['notes'] ?? null,
                'device_id' => $data['device_id'] ?? null,
            ]);

            foreach ($data['items'] as $item) {
                $lineSubTotal = round((float) $item['quantity_ordered'] * (float) $item['unit_cost'], 2);
                $lineTax = round($lineSubTotal * ((float) ($item['tax_rate'] ?? 0) / 100), 2);
                $subtotal += $lineSubTotal;
                $taxAmount += $lineTax;

                $purchaseOrder->items()->create([
                    'product_id' => $item['product_id'],
                    'variant_id' => $item['variant_id'] ?? null,
                    'quantity_ordered' => $item['quantity_ordered'],
                    'unit_cost' => $item['unit_cost'],
                    'tax_rate' => $item['tax_rate'] ?? 0,
                    'tax_amount' => $lineTax,
                    'total_amount' => $lineSubTotal + $lineTax,
                    'expiry_date' => $item['expiry_date'] ?? null,
                    'batch_number' => $item['batch_number'] ?? null,
                ]);
            }

            $purchaseOrder->forceFill([
                'subtotal' => $subtotal,
                'tax_amount' => $taxAmount,
                'total_amount' => round(
                    $subtotal
                    + $taxAmount
                    + (float) $purchaseOrder->shipping_cost
                    - (float) $purchaseOrder->discount_amount,
                    2
                ),
            ])->save();

            $this->auditLogService->log($user, 'purchase_order.created', $purchaseOrder, [], $purchaseOrder->toArray(), $purchaseOrder->device_id);

            return $purchaseOrder->load(['items', 'supplier']);
        });
    }

    public function receive(User $user, PurchaseOrder $purchaseOrder, array $data): PurchaseOrder
    {
        return DB::transaction(function () use ($user, $purchaseOrder, $data) {
            foreach ($data['items'] as $payload) {
                $item = $purchaseOrder->items()->findOrFail($payload['purchase_order_item_id']);
                $receiveQty = (float) $payload['quantity_received'];
                $remaining = (float) $item->quantity_ordered - (float) $item->quantity_received;

                if ($receiveQty <= 0 || $receiveQty > $remaining) {
                    throw ValidationException::withMessages([
                        'items' => ["Invalid received quantity for PO item {$item->id}."],
                    ]);
                }

                $item->increment('quantity_received', $receiveQty);
                $product = Product::findOrFail($item->product_id);

                $batchId = null;

                if (! empty($payload['batch_number']) || ! empty($item->batch_number)) {
                    $batch = Batch::create([
                        'product_id' => $item->product_id,
                        'branch_id' => $purchaseOrder->branch_id,
                        'batch_number' => $payload['batch_number'] ?? $item->batch_number,
                        'expires_at' => $payload['expiry_date'] ?? $item->expiry_date,
                        'quantity' => $receiveQty,
                        'cost_price' => $item->unit_cost,
                    ]);

                    $batchId = $batch->id;
                }

                $this->inventoryService->adjust(
                    user: $user,
                    branchId: $purchaseOrder->branch_id,
                    product: $product,
                    variantId: $item->variant_id,
                    quantityDelta: $receiveQty,
                    type: 'purchase',
                    unitCost: (float) $item->unit_cost,
                    batchId: $batchId,
                    referenceType: PurchaseOrder::class,
                    referenceId: $purchaseOrder->id,
                    notes: $data['notes'] ?? 'Purchase order receiving',
                    deviceId: $purchaseOrder->device_id,
                );
            }

            $purchaseOrder->refresh();

            $status = 'received';
            if ($purchaseOrder->items->contains(fn ($item) => (float) $item->quantity_received < (float) $item->quantity_ordered)) {
                $status = 'partial';
            }

            $purchaseOrder->forceFill([
                'status' => $status,
                'received_at' => now(),
            ])->save();

            $this->auditLogService->log($user, 'purchase_order.received', $purchaseOrder, [], $purchaseOrder->toArray(), $purchaseOrder->device_id);

            return $purchaseOrder->load(['items', 'supplier']);
        });
    }

    private function generateReference(): string
    {
        return 'PO-'.now()->format('YmdHis').'-'.str_pad((string) random_int(1, 9999), 4, '0', STR_PAD_LEFT);
    }
}
