<?php

declare(strict_types=1);

namespace App\Models;

use App\Business\Product\DberpDocumentLineUnitService;
use App\Business\Product\ProductBusinessSnapshot;
use App\Business\Purchase\Enums\PurchaseOrderItemExecutionStatus;
use App\Business\Purchase\Enums\PurchaseOrderStatus;
use App\Business\Purchase\PurchaseSourceDocumentService;
use App\Models\Concerns\HasDberpDocumentLineUnitRelations;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class DberpPurchaseOrderItem extends Model
{
    use HasDberpDocumentLineUnitRelations;

    protected $table = 'dberp_purchase_order_items';

    /**
     * @var list<string>
     */
    protected $fillable = [
        'order_id',
        'purchase_request_item_id',
        'requested_warehouse_id',
        'source_inquiry_quote_id',
        'product_id',
        'product_sku_id',
        'product_code',
        'product_name',
        'sku_code',
        'spec_text',
        'unit_id',
        'unit_name',
        'external_product_id',
        'external_sku',
        'quantity',
        'unit_price',
        'line_amount',
        'arrived_quantity',
        'rejected_quantity',
        'remaining_arrival_quantity',
        'is_completed',
        'expected_arrival_date',
        'last_arrival_date',
        'execution_status',
        'price_hit_result',
        'price_hit_label',
        'price_hit_summary',
        'matched_reference_price',
        'recent_supplier_transaction_unit_price',
        'recent_product_transaction_unit_price',
        'lowest_inquiry_unit_price',
        'recent_supplier_transaction_date',
        'recent_product_transaction_date',
        'recent_supplier_lead_time_days',
        'price_reference_snapshot',
        'received_quantity',
        'returned_quantity',
        'base_quantity',
        'base_received_quantity',
        'base_unit_id',
        'auxiliary_unit_id',
        'is_auxiliary_unit',
        'auxiliary_unit_quantity',
        'base_unit_quantity',
        'sort_order',
        'remark',
    ];

    protected static function booted(): void
    {
        static::created(function (self $item): void {
            app(PurchaseSourceDocumentService::class)->syncOccupationForPurchaseOrderItem($item);
        });

        static::updated(function (self $item): void {
            if ($item->wasChanged(['quantity', 'purchase_request_item_id', 'source_inquiry_quote_id'])) {
                app(PurchaseSourceDocumentService::class)->syncOccupationForPurchaseOrderItem($item);
            }
        });

        static::deleted(function (self $item): void {
            app(PurchaseSourceDocumentService::class)
                ->reverseOccupationForPurchaseOrderItem(
                    $item,
                    (string) __('dberp.messages.purchase_conversion_item_removed'),
                );
        });

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

            DberpDocumentLineUnitService::applySnapshotsToLine($item);

            $quantity = (float) ($item->quantity ?? 0);
            $arrivedQuantity = (float) ($item->arrived_quantity ?? 0);
            $receivedQuantity = (float) ($item->received_quantity ?? 0);
            $remainingArrivalQuantity = max($quantity - $receivedQuantity, 0);
            $unitPrice = (float) ($item->unit_price ?? 0);

            $item->setAttribute('line_amount', number_format($quantity * $unitPrice, 4, '.', ''));
            $item->setAttribute('arrived_quantity', number_format($arrivedQuantity, 4, '.', ''));
            $item->setAttribute('received_quantity', number_format($receivedQuantity, 4, '.', ''));
            $item->setAttribute('rejected_quantity', number_format((float) ($item->rejected_quantity ?? 0), 4, '.', ''));
            $item->setAttribute('remaining_arrival_quantity', number_format($remainingArrivalQuantity, 4, '.', ''));
            $item->setAttribute('is_completed', $remainingArrivalQuantity <= 0.00005);

            if ($item->execution_status === null || $item->execution_status === '') {
                $orderStatus = $item->relationLoaded('order') ? $item->order?->status : $item->order()->value('status');

                $item->setAttribute('execution_status', match ($orderStatus) {
                    PurchaseOrderStatus::Voided->value => PurchaseOrderItemExecutionStatus::Voided->value,
                    PurchaseOrderStatus::Closed->value => PurchaseOrderItemExecutionStatus::Closed->value,
                    PurchaseOrderStatus::Draft->value, null => PurchaseOrderItemExecutionStatus::AwaitingRelease->value,
                    default => $remainingArrivalQuantity <= 0.00005
                        ? PurchaseOrderItemExecutionStatus::Completed->value
                        : PurchaseOrderItemExecutionStatus::PendingArrival->value,
                });
            }
        });

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

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

    protected function casts(): array
    {
        return [
            'order_id' => 'integer',
            'purchase_request_item_id' => 'integer',
            'requested_warehouse_id' => 'integer',
            'source_inquiry_quote_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',
            'quantity' => 'decimal:4',
            'unit_price' => 'decimal:4',
            'line_amount' => 'decimal:4',
            'arrived_quantity' => 'decimal:4',
            'rejected_quantity' => 'decimal:4',
            'remaining_arrival_quantity' => 'decimal:4',
            'is_completed' => 'boolean',
            'expected_arrival_date' => 'date',
            'last_arrival_date' => 'date',
            'matched_reference_price' => 'decimal:4',
            'recent_supplier_transaction_unit_price' => 'decimal:4',
            'recent_product_transaction_unit_price' => 'decimal:4',
            'lowest_inquiry_unit_price' => 'decimal:4',
            'recent_supplier_transaction_date' => 'date',
            'recent_product_transaction_date' => 'date',
            'recent_supplier_lead_time_days' => 'integer',
            'price_reference_snapshot' => 'array',
            'received_quantity' => 'decimal:4',
            'returned_quantity' => 'decimal:4',
            'base_quantity' => 'decimal:4',
            'base_received_quantity' => 'decimal:4',
            'sort_order' => 'integer',
        ];
    }

    public function order(): BelongsTo
    {
        return $this->belongsTo(DberpPurchaseOrder::class, 'order_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 purchaseExceptions(): HasMany
    {
        return $this->hasMany(DberpPurchaseException::class, 'purchase_order_item_id');
    }

    public function purchaseRequestItem(): BelongsTo
    {
        return $this->belongsTo(DberpPurchaseRequestItem::class, 'purchase_request_item_id');
    }

    public function requestedWarehouse(): BelongsTo
    {
        return $this->belongsTo(DberpWarehouse::class, 'requested_warehouse_id');
    }

    public function sourceInquiryQuote(): BelongsTo
    {
        return $this->belongsTo(DberpPurchaseInquiryQuote::class, 'source_inquiry_quote_id');
    }

    public function conversionLines(): HasMany
    {
        return $this->hasMany(DberpPurchaseConversionLine::class, 'purchase_order_item_id');
    }
}
