<?php

declare(strict_types=1);

namespace App\Models;

use App\Business\Inventory\Enums\InventoryDocumentStatus;
use App\Business\Inventory\Enums\StocktakeScopeType;
use App\Models\Concerns\GeneratesDberpCode;
use App\Models\Concerns\HasDberpAuditTrail;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Validation\ValidationException;

class DberpStocktakeOrder extends Model
{
    use GeneratesDberpCode;
    use HasDberpAuditTrail;

    protected $table = 'dberp_stocktake_orders';

    /**
     * @var list<string>
     */
    protected $fillable = [
        'code',
        'warehouse_id',
        'scope_type',
        'scope_category_id',
        'scope_product_id',
        'stocktake_employee_id',
        'status',
        'business_time',
        'effective_at',
        'voided_at',
        'remark',
    ];

    protected static function booted(): void
    {
        static::deleting(function (self $order): void {
            if ($order->status !== InventoryDocumentStatus::Draft->value) {
                throw ValidationException::withMessages([
                    'status' => __('dberp.errors.inventory_delete_only_draft'),
                ]);
            }
        });
    }

    protected function casts(): array
    {
        return [
            'warehouse_id' => 'integer',
            'scope_category_id' => 'integer',
            'scope_product_id' => 'integer',
            'stocktake_employee_id' => 'integer',
            'business_time' => 'datetime',
            'effective_at' => 'datetime',
            'voided_at' => 'datetime',
        ];
    }

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

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

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

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

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

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

    public function stocktakeEmployee(): BelongsTo
    {
        return $this->belongsTo(DberpEmployee::class, 'stocktake_employee_id');
    }

    public function items(): HasMany
    {
        return $this->hasMany(DberpStocktakeOrderItem::class, 'order_id');
    }

    /**
     * @param  array<string, mixed>  $data
     * @return array<string, mixed>
     */
    public static function normalizeScopeFormData(array $data): array
    {
        $scope = StocktakeScopeType::tryFrom((string) ($data['scope_type'] ?? StocktakeScopeType::All->value))
            ?? StocktakeScopeType::All;

        $data['scope_type'] = $scope->value;

        if ($scope !== StocktakeScopeType::Category) {
            $data['scope_category_id'] = null;
        } elseif (array_key_exists('scope_category_id', $data) && ($data['scope_category_id'] === '' || $data['scope_category_id'] === null)) {
            $data['scope_category_id'] = null;
        }

        if ($scope !== StocktakeScopeType::Product) {
            $data['scope_product_id'] = null;
        } elseif (array_key_exists('scope_product_id', $data) && ($data['scope_product_id'] === '' || $data['scope_product_id'] === null)) {
            $data['scope_product_id'] = null;
        }

        return $data;
    }

    public function assertScopeFieldsForSave(): void
    {
        $scope = StocktakeScopeType::tryFrom((string) $this->scope_type) ?? StocktakeScopeType::All;

        if ($scope === StocktakeScopeType::Category && $this->scope_category_id === null) {
            throw ValidationException::withMessages([
                'scope_category_id' => __('dberp.errors.stocktake_scope_category_required'),
            ]);
        }

        if ($scope === StocktakeScopeType::Product && $this->scope_product_id === null) {
            throw ValidationException::withMessages([
                'scope_product_id' => __('dberp.errors.stocktake_scope_product_required'),
            ]);
        }
    }

    /**
     * @param  Builder<DberpProduct>  $query
     * @return Builder<DberpProduct>
     */
    public function applyScopeToProductQuery(Builder $query): Builder
    {
        $scope = StocktakeScopeType::tryFrom((string) $this->scope_type) ?? StocktakeScopeType::All;

        if ($scope === StocktakeScopeType::Category && $this->scope_category_id !== null) {
            return $query->where('category_id', (int) $this->scope_category_id);
        }

        if ($scope === StocktakeScopeType::Product && $this->scope_product_id !== null) {
            return $query->whereKey((int) $this->scope_product_id);
        }

        return $query;
    }

    public function assertLineProductWithinScope(DberpProduct $product): void
    {
        $scope = StocktakeScopeType::tryFrom((string) $this->scope_type) ?? StocktakeScopeType::All;

        if ($scope === StocktakeScopeType::Category && (int) $product->category_id !== (int) $this->scope_category_id) {
            throw ValidationException::withMessages([
                'product_id' => __('dberp.errors.stocktake_scope_category_mismatch'),
            ]);
        }

        if ($scope === StocktakeScopeType::Product && (int) $product->getKey() !== (int) $this->scope_product_id) {
            throw ValidationException::withMessages([
                'product_id' => __('dberp.errors.stocktake_scope_product_mismatch'),
            ]);
        }
    }
}
