<?php

declare(strict_types=1);

namespace App\Models;

use App\Business\Product\ProductSkuReferenceGuard;
use App\Business\SystemRule\NumberSequenceAllocator;
use App\Models\Concerns\HasDberpAuditTrail;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Support\Facades\DB;

class DberpProductSku extends Model
{
    use HasDberpAuditTrail;

    protected $table = 'dberp_product_skus';

    /**
     * @var list<string>
     */
    protected $fillable = [
        'product_id',
        'sku_code',
        'barcode',
        'spec_text',
        'cost_price',
        'sales_price',
        'unit_id',
        'is_enabled',
        'is_default',
        'sort_order',
        'remark',
    ];

    protected static function booted(): void
    {
        static::creating(function (self $sku): void {
            $barcode = $sku->getAttribute('barcode');
            if ($barcode === '') {
                $sku->setAttribute('barcode', null);
            }

            $skuCode = trim((string) ($sku->getAttribute('sku_code') ?? ''));
            if ($skuCode === '') {
                $allocated = app(NumberSequenceAllocator::class)->reserveByAppliesTo('product_sku');
                if ($allocated === null) {
                    if (app()->runningUnitTests()) {
                        $allocated = 'TSK'.strtoupper(bin2hex(random_bytes(4)));
                    } else {
                        throw new \RuntimeException((string) __('dberp.errors.product_sku_code_rule_missing'));
                    }
                }

                $sku->setAttribute('sku_code', $allocated);
            }

            $productId = (int) ($sku->getAttribute('product_id') ?? 0);
            if ($productId > 0) {
                $exists = static::query()->where('product_id', $productId)->exists();
                if (! $exists) {
                    $sku->setAttribute('is_default', true);
                }
            }
        });

        static::saving(function (self $sku): void {
            $barcode = $sku->getAttribute('barcode');
            if ($barcode === '') {
                $sku->setAttribute('barcode', null);
            }

            if ($sku->isDirty('is_enabled') && ! $sku->is_enabled) {
                ProductSkuReferenceGuard::assertCanDisable($sku);
            }

            if ($sku->getAttribute('is_default')) {
                $productId = (int) ($sku->getAttribute('product_id') ?? 0);
                if ($productId > 0) {
                    $query = static::query()->where('product_id', $productId)->where('is_default', true);
                    if ($sku->exists) {
                        $query->whereKeyNot($sku->getKey());
                    }

                    $query->update(['is_default' => false]);
                }
            }
        });

        static::deleting(function (self $sku): void {
            ProductSkuReferenceGuard::assertCanPhysicallyDelete($sku);
        });
    }

    protected function casts(): array
    {
        return [
            'product_id' => 'integer',
            'unit_id' => 'integer',
            'cost_price' => 'decimal:4',
            'sales_price' => 'decimal:4',
            'is_enabled' => 'boolean',
            'is_default' => 'boolean',
            'sort_order' => 'integer',
        ];
    }

    public function getDberpAuditActionPrefix(): string
    {
        return 'dberp.product_sku';
    }

    /**
     * @return list<string>
     */
    public function getDberpAuditFields(): array
    {
        return $this->getFillable();
    }

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

    public function unit(): BelongsTo
    {
        return $this->belongsTo(DberpUnit::class, 'unit_id');
    }

    public function skuSpecAssignments(): HasMany
    {
        return $this->hasMany(DberpProductSkuSpecValue::class, 'product_sku_id');
    }

    public function specValues(): BelongsToMany
    {
        return $this->belongsToMany(
            DberpProductSpecValue::class,
            'dberp_product_sku_spec_values',
            'product_sku_id',
            'product_spec_value_id',
        )->withTimestamps();
    }

    public function resolveEffectiveUnit(): ?DberpUnit
    {
        if ($this->relationLoaded('unit') && $this->unit !== null) {
            return $this->unit;
        }

        if ($this->unit_id !== null) {
            return $this->unit()->first();
        }

        $this->loadMissing('product.unit');

        return $this->product?->unit;
    }

    /**
     * 在事务内为物料创建唯一默认 SKU（单规格场景），规格文本默认沿用物料规格型号承载位。
     */
    public static function createDefaultForProduct(DberpProduct $product): self
    {
        return DB::transaction(function () use ($product): self {
            $exists = static::query()->where('product_id', $product->getKey())->exists();
            if ($exists) {
                $default = static::query()
                    ->where('product_id', $product->getKey())
                    ->where('is_default', true)
                    ->orderBy('sort_order')
                    ->orderBy('id')
                    ->first();

                if ($default !== null) {
                    return $default;
                }

                return static::query()
                    ->where('product_id', $product->getKey())
                    ->orderBy('sort_order')
                    ->orderBy('id')
                    ->firstOrFail();
            }

            return static::query()->create([
                'product_id' => $product->getKey(),
                'sku_code' => '',
                'spec_text' => $product->spec_model,
                'cost_price' => $product->cost_price,
                'sales_price' => $product->sales_price,
                'unit_id' => null,
                'is_enabled' => $product->is_enabled,
                'is_default' => true,
                'sort_order' => 0,
            ]);
        });
    }
}
