<?php

namespace Modules\SystemCore\App\Models;

use App\Models\User;
use App\Traits\UserActionsTrait;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\SoftDeletes;
use Modules\SystemCore\App\Enums\OrderStatus;
use OwenIt\Auditing\Auditable;
use OwenIt\Auditing\Contracts\Auditable as AuditableContract;

class CorePurchaseOrder extends Model implements AuditableContract
{
    use Auditable, SoftDeletes, UserActionsTrait;

    protected $table = 'core_purchase_orders';

    protected $fillable = [
        'order_number',
        'reference_no',
        'invoice_no',
        'supplier_id',
        'warehouse_id',
        'payment_term_id',
        'user_id',
        'order_date',
        'expected_delivery_date',
        'actual_delivery_date',
        'status',
        'subtotal',
        'tax_amount',
        'discount_amount',
        'shipping_cost',
        'total_amount',
        'payment_status',
        'payment_method',
        'payment_terms',
        'payment_due_date',
        'paid_amount',
        'shipping_method',
        'tracking_number',
        'approval_status',
        'approved_by_id',
        'approved_at',
        'received_by_id',
        'received_at',
        'notes',
        'metadata',
        'created_by_id',
        'updated_by_id',
    ];

    protected function casts(): array
    {
        return [
            'status' => OrderStatus::class,
            'order_date' => 'datetime',
            'expected_delivery_date' => 'date',
            'actual_delivery_date' => 'date',
            'payment_due_date' => 'date',
            'approved_at' => 'datetime',
            'received_at' => 'datetime',
            'subtotal' => 'decimal:2',
            'tax_amount' => 'decimal:2',
            'discount_amount' => 'decimal:2',
            'shipping_cost' => 'decimal:2',
            'total_amount' => 'decimal:2',
            'paid_amount' => 'decimal:2',
            'metadata' => 'array',
        ];
    }

    /**
     * Get supplier
     */
    public function supplier(): BelongsTo
    {
        return $this->belongsTo(CoreSupplier::class, 'supplier_id');
    }

    /**
     * Get payment term
     */
    public function paymentTerm(): BelongsTo
    {
        return $this->belongsTo(CorePaymentTerm::class, 'payment_term_id');
    }

    /**
     * Get the user who created the order
     */
    public function user(): BelongsTo
    {
        return $this->belongsTo(User::class, 'user_id');
    }

    /**
     * Get the warehouse (requires WMSInventoryCore module)
     */
    public function warehouse(): BelongsTo
    {
        $warehouseClass = '\\Modules\\WMSInventoryCore\\App\\Models\\Warehouse';
        if (moduleExists('WMSInventoryCore') && class_exists($warehouseClass)) {
            return $this->belongsTo($warehouseClass, 'warehouse_id');
        }

        // Return empty relation when WMSInventoryCore is not available
        return $this->belongsTo(CoreSupplier::class, 'warehouse_id')->whereRaw('1=0');
    }

    /**
     * Get the user who approved this order
     */
    public function approvedBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'approved_by_id');
    }

    /**
     * Get the user who received this order
     */
    public function receivedBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'received_by_id');
    }

    /**
     * Get order lines
     */
    public function orderLines(): HasMany
    {
        return $this->hasMany(CorePurchaseOrderLine::class, 'purchase_order_id');
    }

    /**
     * Get the accounting transaction for this order (requires AccountingCore module)
     */
    public function accountingTransaction(): MorphOne
    {
        $transactionClass = '\\Modules\\AccountingCore\\App\\Models\\BasicTransaction';
        if (moduleExists('AccountingCore') && class_exists($transactionClass)) {
            return $this->morphOne($transactionClass, 'sourceable');
        }

        // Return empty relation when AccountingCore is not available
        return $this->morphOne(CorePurchaseOrderLine::class, 'sourceable')->whereRaw('1=0');
    }

    /**
     * Get the remaining balance to pay
     */
    public function getRemainingBalanceAttribute(): float
    {
        return (float) $this->total_amount - (float) $this->paid_amount;
    }

    /**
     * Check if this order is fully paid
     */
    public function isFullyPaid(): bool
    {
        return $this->payment_status === 'paid' || $this->remaining_balance <= 0;
    }

    /**
     * Check if this order is fully received
     */
    public function isFullyReceived(): bool
    {
        foreach ($this->orderLines as $line) {
            if (! ($line->is_received ?? false)) {
                return false;
            }
        }

        return true;
    }

    /**
     * Get the percentage of items received
     */
    public function getReceivingProgressAttribute(): float
    {
        $totalItems = $this->orderLines->count();
        if ($totalItems === 0) {
            return 0.0;
        }

        $receivedItems = $this->orderLines->where('is_received', true)->count();

        return ($receivedItems / $totalItems) * 100;
    }

    /**
     * Generate order number using settings
     */
    public static function generateOrderNumber(): string
    {
        $settingsService = app(\App\Services\Settings\ModuleSettingsService::class);
        $prefix = $settingsService->get('SystemCore', 'purchase_order_prefix', 'PO');
        $padding = (int) $settingsService->get('SystemCore', 'order_number_padding', 5);
        $includeDate = (bool) $settingsService->get('SystemCore', 'include_date_in_order_number', true);

        $lastOrder = self::orderBy('id', 'desc')->first();
        $nextId = $lastOrder ? $lastOrder->id + 1 : 1;

        if ($includeDate) {
            return $prefix.'-'.date('Ymd').'-'.str_pad((string) $nextId, $padding, '0', STR_PAD_LEFT);
        }

        return $prefix.'-'.str_pad((string) $nextId, $padding, '0', STR_PAD_LEFT);
    }

    /**
     * Boot method
     */
    protected static function booted()
    {
        static::creating(function (CorePurchaseOrder $order) {
            if (! $order->order_number) {
                $order->order_number = self::generateOrderNumber();
            }
        });

        // Dispatch event after the model is saved when payment_status changes
        static::updated(function (CorePurchaseOrder $order) {
            if ($order->wasChanged('payment_status')) {
                event(new \Modules\SystemCore\App\Events\PurchaseOrderPaymentStatusChanged(
                    $order,
                    $order->getOriginal('payment_status'),
                    $order->payment_status
                ));
            }
        });
    }
}
