<?php

declare(strict_types=1);

namespace App\Models;

use App\Models\Concerns\GeneratesDberpCode;
use App\Models\Concerns\HasDberpAuditTrail;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasOne;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Validation\ValidationException;

class DberpProduct extends Model
{
    use GeneratesDberpCode;
    use HasDberpAuditTrail;

    protected $table = 'dberp_products';

    /**
     * @var list<string>
     */
    protected $fillable = [
        'code',
        'name',
        'barcode',
        'mnemonic_code',
        'category_id',
        'brand_id',
        'brand_name',
        'spec_enabled',
        'spec_model',
        'unit_id',
        'cost_price',
        'sales_price',
        'source_platform',
        'external_product_id',
        'external_sku',
        'external_spec',
        'integration_sync_status',
        'last_integration_synced_at',
        'is_enabled',
        'sort_order',
        'remark',
        'can_purchase',
        'can_sale',
        'manages_inventory',
        'manages_batch',
        'manages_serial_number',
        'manages_expiry',
        'expiry_warning_days',
        'inventory_issue_policy',
        'is_auxiliary_unit_enabled',
        'auxiliary_unit_id',
        'auxiliary_unit_quantity',
        'base_unit_quantity',
    ];

    protected function casts(): array
    {
        return [
            'category_id' => 'integer',
            'brand_id' => 'integer',
            'unit_id' => 'integer',
            'cost_price' => 'decimal:4',
            'sales_price' => 'decimal:4',
            'is_enabled' => 'boolean',
            'sort_order' => 'integer',
            'can_purchase' => 'boolean',
            'can_sale' => 'boolean',
            'manages_inventory' => 'boolean',
            'manages_batch' => 'boolean',
            'manages_serial_number' => 'boolean',
            'manages_expiry' => 'boolean',
            'expiry_warning_days' => 'integer',
            'last_integration_synced_at' => 'datetime',
            'spec_enabled' => 'boolean',
            'is_auxiliary_unit_enabled' => 'boolean',
            'auxiliary_unit_id' => 'integer',
            'auxiliary_unit_quantity' => 'decimal:6',
            'base_unit_quantity' => 'decimal:6',
        ];
    }

    public static function booted(): void
    {
        static::saving(function (self $product): void {
            $product->normalizeAuxiliaryUnitAttributesForPersistence();
            $product->assertBrandSelectionIsAllowed();
            $product->assertAuxiliaryUnitConfigurationIsValid();
        });
    }

    public function getDberpNumberScope(): string
    {
        return 'product';
    }

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

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

    public function category(): BelongsTo
    {
        return $this->belongsTo(DberpProductCategory::class, 'category_id');
    }

    public function brand(): BelongsTo
    {
        return $this->belongsTo(DberpProductBrand::class, 'brand_id');
    }

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

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

    public function normalizeAuxiliaryUnitAttributesForPersistence(): void
    {
        $this->setAttribute('is_auxiliary_unit_enabled', (bool) $this->getAttribute('is_auxiliary_unit_enabled'));

        if (! (bool) $this->getAttribute('is_auxiliary_unit_enabled')) {
            $this->setAttribute('auxiliary_unit_id', null);
            $this->setAttribute('auxiliary_unit_quantity', null);
            $this->setAttribute('base_unit_quantity', null);

            return;
        }

        foreach (['auxiliary_unit_quantity', 'base_unit_quantity'] as $key) {
            $value = $this->getAttribute($key);
            if ($value === '' || $value === null) {
                $this->setAttribute($key, null);
            }
        }

        $auxiliaryUnitId = $this->getAttribute('auxiliary_unit_id');
        if ($auxiliaryUnitId === '' || $auxiliaryUnitId === null) {
            $this->setAttribute('auxiliary_unit_id', null);
        } else {
            $this->setAttribute('auxiliary_unit_id', (int) $auxiliaryUnitId);
        }
    }

    public function assertAuxiliaryUnitConfigurationIsValid(): void
    {
        if (! (bool) $this->getAttribute('is_auxiliary_unit_enabled')) {
            return;
        }

        $baseUnitId = (int) $this->getAttribute('unit_id');
        if ($baseUnitId <= 0) {
            $message = (string) __('dberp.validation.product_base_unit_required_before_auxiliary');
            throw ValidationException::withMessages([
                'data.unit_id' => $message,
                'unit_id' => $message,
            ]);
        }

        $auxiliaryUnitId = $this->getAttribute('auxiliary_unit_id');
        if ($auxiliaryUnitId === null || (int) $auxiliaryUnitId <= 0) {
            $message = (string) __('dberp.validation.product_auxiliary_unit_required');
            throw ValidationException::withMessages([
                'data.auxiliary_unit_id' => $message,
                'auxiliary_unit_id' => $message,
            ]);
        }

        $auxiliaryUnitId = (int) $auxiliaryUnitId;

        if ($auxiliaryUnitId === $baseUnitId) {
            $message = (string) __('dberp.validation.product_auxiliary_unit_must_differ_from_base');
            throw ValidationException::withMessages([
                'data.auxiliary_unit_id' => $message,
                'auxiliary_unit_id' => $message,
            ]);
        }

        $auxiliaryUnit = DberpUnit::query()->find($auxiliaryUnitId);
        if ($auxiliaryUnit === null) {
            $message = (string) __('dberp.validation.product_auxiliary_unit_missing');
            throw ValidationException::withMessages([
                'data.auxiliary_unit_id' => $message,
                'auxiliary_unit_id' => $message,
            ]);
        }

        if (! (bool) $auxiliaryUnit->is_enabled) {
            $message = (string) __('dberp.validation.product_auxiliary_unit_must_be_enabled');
            throw ValidationException::withMessages([
                'data.auxiliary_unit_id' => $message,
                'auxiliary_unit_id' => $message,
            ]);
        }

        $auxiliaryQty = $this->getAttribute('auxiliary_unit_quantity');
        $baseQty = $this->getAttribute('base_unit_quantity');

        if ($auxiliaryQty === null || $baseQty === null) {
            $message = (string) __('dberp.validation.product_auxiliary_conversion_quantities_required');
            throw ValidationException::withMessages([
                'data.auxiliary_unit_quantity' => $message,
                'auxiliary_unit_quantity' => $message,
                'data.base_unit_quantity' => $message,
                'base_unit_quantity' => $message,
            ]);
        }

        if (! $this->isStrictlyPositiveDecimal($auxiliaryQty)) {
            $message = (string) __('dberp.validation.product_auxiliary_quantity_must_be_positive');
            throw ValidationException::withMessages([
                'data.auxiliary_unit_quantity' => $message,
                'auxiliary_unit_quantity' => $message,
            ]);
        }

        if (! $this->isStrictlyPositiveDecimal($baseQty)) {
            $message = (string) __('dberp.validation.product_base_unit_quantity_must_be_positive');
            throw ValidationException::withMessages([
                'data.base_unit_quantity' => $message,
                'base_unit_quantity' => $message,
            ]);
        }
    }

    private function isStrictlyPositiveDecimal(mixed $value): bool
    {
        if ($value === null || $value === '') {
            return false;
        }

        $normalized = is_numeric($value) ? (string) $value : '';

        if ($normalized === '') {
            return false;
        }

        return bccomp($normalized, '0', 6) === 1;
    }

    public function getBusinessBrandDisplay(): string
    {
        if ($this->brand_id === null) {
            $legacy = trim((string) $this->brand_name);

            return $legacy !== '' ? $legacy : '—';
        }

        $brand = $this->relationLoaded('brand')
            ? $this->getRelation('brand')
            : $this->brand()->first();

        if ($brand instanceof DberpProductBrand) {
            return (string) $brand->name;
        }

        $legacy = trim((string) $this->brand_name);

        return $legacy !== '' ? $legacy : '—';
    }

    public function assertBrandSelectionIsAllowed(): void
    {
        $brandId = $this->brand_id;

        if ($brandId === null) {
            return;
        }

        $brand = DberpProductBrand::query()->find((int) $brandId);

        if ($brand === null) {
            $message = (string) __('dberp.validation.product_brand_missing');
            throw ValidationException::withMessages([
                'data.brand_id' => $message,
                'brand_id' => $message,
            ]);
        }

        if ($brand->is_enabled) {
            return;
        }

        if ($this->exists && (int) $this->getOriginal('brand_id') === (int) $brandId) {
            return;
        }

        $message = (string) __('dberp.validation.product_brand_must_be_enabled');
        throw ValidationException::withMessages([
            'data.brand_id' => $message,
            'brand_id' => $message,
        ]);
    }

    public function skus(): HasMany
    {
        return $this->hasMany(DberpProductSku::class, 'product_id')->orderBy('sort_order')->orderBy('id');
    }

    public function defaultSku(): HasOne
    {
        return $this->hasOne(DberpProductSku::class, 'product_id')->where('is_default', true);
    }

    public function specGroups(): HasMany
    {
        return $this->hasMany(DberpProductSpecGroup::class, 'product_id')->orderBy('sort_order')->orderBy('id');
    }

    public function inventoryBalances(): HasMany
    {
        return $this->hasMany(DberpInventoryBalance::class, 'product_id');
    }

    public function inventoryBatches(): HasMany
    {
        return $this->hasMany(DberpInventoryBatch::class, 'product_id');
    }

    public function inventorySerialNumbers(): HasMany
    {
        return $this->hasMany(DberpInventorySerialNumber::class, 'product_id');
    }

    public function inventoryRules(): HasMany
    {
        return $this->hasMany(DberpInventoryRule::class, 'product_id');
    }

    public function replenishmentSuggestions(): HasMany
    {
        return $this->hasMany(DberpInventoryReplenishmentSuggestion::class, 'product_id');
    }

    public function integrationMappings(): MorphMany
    {
        return $this->morphMany(DberpIntegrationMapping::class, 'localModel', 'local_model_type', 'local_model_id');
    }
}
