<?php

declare(strict_types=1);

namespace App\Models;

use App\Business\Product\DberpDocumentLineUnitService;
use App\Business\Product\ProductBusinessSnapshot;
use App\Models\Concerns\HasDberpDocumentLineUnitRelations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasOne;

class DberpPurchaseReceiptOrderItem extends Model
{
    use HasDberpDocumentLineUnitRelations;

    protected $table = 'dberp_purchase_receipt_order_items';

    /**
     * @var list<string>
     */
    protected $fillable = [
        'order_id',
        'purchase_order_item_id',
        'product_id',
        'product_sku_id',
        'product_code',
        'product_name',
        'sku_code',
        'spec_text',
        'unit_id',
        'unit_name',
        'base_unit_id',
        'auxiliary_unit_id',
        'is_auxiliary_unit',
        'auxiliary_unit_quantity',
        'base_unit_quantity',
        'source_batch_id',
        'batch_no',
        'production_date',
        'expiry_date',
        'shelf_life_days',
        'batch_allocations',
        'serial_numbers',
        'arrival_quantity',
        'received_quantity',
        'exception_type',
        'exception_reason_detail',
        'handling_suggestion',
        'unit_price',
        'line_amount',
        'returned_quantity',
        'base_received_quantity',
        'base_arrival_quantity',
        'base_returned_quantity',
        'sort_order',
        'remark',
    ];

    protected static function booted(): void
    {
        static::saving(function (self $item): void {
            $purchaseOrderItemId = (int) ($item->purchase_order_item_id ?? 0);
            if ($purchaseOrderItemId > 0) {
                $poLine = $item->relationLoaded('purchaseOrderItem')
                    ? $item->purchaseOrderItem
                    : DberpPurchaseOrderItem::query()->find($purchaseOrderItemId);

                if ($poLine instanceof DberpPurchaseOrderItem) {
                    $item->setAttribute('product_id', $poLine->product_id);
                    if ($poLine->product_sku_id !== null) {
                        $sku = DberpProductSku::query()->with(['product.unit'])->find($poLine->product_sku_id);
                        if ($sku instanceof DberpProductSku) {
                            $snap = ProductBusinessSnapshot::identityFromSku($sku);
                            $item->forceFill([
                                'product_sku_id' => $snap['product_sku_id'],
                                'product_code' => $snap['product_code'],
                                'product_name' => $snap['product_name'],
                                'sku_code' => $snap['sku_code'],
                                'spec_text' => $snap['spec_text'],
                            ]);
                        }
                    }
                }
            }

            DberpDocumentLineUnitService::applySnapshotsToLine($item);

            $received = (float) ($item->received_quantity ?? 0);
            $unitPrice = (float) ($item->unit_price ?? 0);

            $item->setAttribute('line_amount', number_format($received * $unitPrice, 4, '.', ''));
        });

        static::saved(function (self $item): void {
            $item->order?->forceFill([
                'total_arrival_quantity' => (string) $item->order->items()->sum('arrival_quantity'),
                'total_received_quantity' => (string) $item->order->items()->sum('received_quantity'),
                'total_amount' => (string) $item->order->items()->sum('line_amount'),
            ])->save();
        });

        static::deleted(function (self $item): void {
            $item->order?->forceFill([
                'total_arrival_quantity' => (string) $item->order->items()->sum('arrival_quantity'),
                'total_received_quantity' => (string) $item->order->items()->sum('received_quantity'),
                'total_amount' => (string) $item->order->items()->sum('line_amount'),
            ])->save();
        });
    }

    protected function casts(): array
    {
        return [
            'order_id' => 'integer',
            'purchase_order_item_id' => 'integer',
            'product_id' => 'integer',
            'product_sku_id' => 'integer',
            'unit_id' => 'integer',
            'base_unit_id' => 'integer',
            'auxiliary_unit_id' => 'integer',
            'is_auxiliary_unit' => 'boolean',
            'auxiliary_unit_quantity' => 'decimal:6',
            'base_unit_quantity' => 'decimal:6',
            'source_batch_id' => 'integer',
            'production_date' => 'date',
            'expiry_date' => 'date',
            'shelf_life_days' => 'integer',
            'batch_allocations' => 'array',
            'serial_numbers' => 'array',
            'arrival_quantity' => 'decimal:4',
            'received_quantity' => 'decimal:4',
            'unit_price' => 'decimal:4',
            'line_amount' => 'decimal:4',
            'returned_quantity' => 'decimal:4',
            'base_received_quantity' => 'decimal:4',
            'base_arrival_quantity' => 'decimal:4',
            'base_returned_quantity' => 'decimal:4',
            'sort_order' => 'integer',
        ];
    }

    public function order(): BelongsTo
    {
        return $this->belongsTo(DberpPurchaseReceiptOrder::class, 'order_id');
    }

    public function purchaseOrderItem(): BelongsTo
    {
        return $this->belongsTo(DberpPurchaseOrderItem::class, 'purchase_order_item_id');
    }

    public function product(): BelongsTo
    {
        return $this->belongsTo(DberpProduct::class, 'product_id');
    }

    public function productSku(): BelongsTo
    {
        return $this->belongsTo(DberpProductSku::class, 'product_sku_id');
    }

    public function sourceBatch(): BelongsTo
    {
        return $this->belongsTo(DberpInventoryBatch::class, 'source_batch_id');
    }

    public function purchaseException(): HasOne
    {
        return $this->hasOne(DberpPurchaseException::class, 'purchase_receipt_item_id');
    }
}
