<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Purchase extends Model
{
    use HasFactory;

    protected $fillable = [
        'material_id',
        'purchase_date',
        'quantity',
        'unit_price',
        'total_price',
        'paid_amount',
        'payment_status',
        'type',
        'supplier',
        'supplier_id',
        'notes',
        'created_by',
    ];

    /**
     * Temporary runtime-only properties for stock recalculation.
     * These are NOT database columns.
     */
    protected ?int $oldMaterialIdForStock = null;
    protected float $oldQuantityForStock = 0;

    protected function casts(): array
    {
        return [
            'purchase_date' => 'date',
            'quantity' => 'decimal:2',
            'unit_price' => 'decimal:2',
            'total_price' => 'decimal:2',
            'paid_amount' => 'decimal:2',
        ];
    }

    const TYPES = [
        'satuan' => 'Satuan (pcs/lembar)',
        'meteran' => 'Meteran (m/m²)',
    ];

    const PAYMENT_STATUSES = [
        'unpaid' => 'Belum Bayar',
        'partial' => 'Sebagian',
        'paid' => 'Lunas',
    ];

    protected static function booted(): void
    {
        static::created(function (Purchase $purchase) {
            $purchase->applyStockIn();
        });

        static::updating(function (Purchase $purchase) {
            $purchase->oldMaterialIdForStock = $purchase->getOriginal('material_id');
            $purchase->oldQuantityForStock = (float) $purchase->getOriginal('quantity');
        });

        static::updated(function (Purchase $purchase) {
            $oldMaterialId = $purchase->oldMaterialIdForStock;
            $oldQuantity = $purchase->oldQuantityForStock;

            $materialChanged = (string) $oldMaterialId !== (string) $purchase->material_id;
            $quantityChanged = round($oldQuantity, 4) !== round((float) $purchase->quantity, 4);

            if (!$materialChanged && !$quantityChanged) {
                return;
            }

            if (!empty($oldMaterialId) && $oldQuantity > 0) {
                Material::where('id', $oldMaterialId)->decrement('stock', $oldQuantity);
            }

            $purchase->applyStockIn();
        });

        static::deleted(function (Purchase $purchase) {
            if ($purchase->material_id && (float) $purchase->quantity > 0) {
                Material::where('id', $purchase->material_id)
                    ->decrement('stock', (float) $purchase->quantity);
            }

            if (method_exists($purchase, 'stockMovement')) {
                $stockMovement = $purchase->stockMovement;
                if ($stockMovement) {
                    $stockMovement->delete();
                }
            }
        });
    }

    protected function applyStockIn(): void
    {
        if (!$this->material_id) {
            return;
        }

        $qty = (float) $this->quantity;

        if ($qty <= 0) {
            return;
        }

        Material::where('id', $this->material_id)->increment('stock', $qty);
    }

    /**
     * Get the stock movement record for this purchase.
     */
    public function stockMovement()
    {
        return $this->morphOne(StockMovement::class, 'reference');
    }

    public function material()
    {
        return $this->belongsTo(Material::class);
    }

    public function supplierRelation()
    {
        return $this->belongsTo(Supplier::class, 'supplier_id');
    }

    public function creator()
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    /**
     * Get all payments for this purchase.
     */
    public function payments()
    {
        return $this->hasMany(PurchasePayment::class)->orderBy('payment_date');
    }

    public function getTypeLabelAttribute(): string
    {
        return self::TYPES[$this->type] ?? $this->type;
    }

    public function getPaymentStatusLabelAttribute(): string
    {
        return self::PAYMENT_STATUSES[$this->payment_status] ?? $this->payment_status;
    }

    public function getFormattedTotalAttribute(): string
    {
        return 'Rp ' . number_format($this->total_price, 0, ',', '.');
    }

    public function getFormattedPaidAttribute(): string
    {
        return 'Rp ' . number_format($this->paid_amount, 0, ',', '.');
    }

    /**
     * Get the remaining unpaid amount.
     */
    public function getRemainingAttribute(): float
    {
        return max(0, (float) $this->total_price - (float) $this->paid_amount);
    }

    public function getFormattedRemainingAttribute(): string
    {
        return 'Rp ' . number_format($this->remaining, 0, ',', '.');
    }

    /**
     * Update payment status based on paid amount.
     */
    public function updatePaymentStatus(): void
    {
        $this->paid_amount = $this->payments()->sum('amount');

        if ((float) $this->paid_amount >= (float) $this->total_price) {
            $this->payment_status = 'paid';
        } elseif ((float) $this->paid_amount > 0) {
            $this->payment_status = 'partial';
        } else {
            $this->payment_status = 'unpaid';
        }

        $this->save();
    }

    /**
     * Check if purchase is fully paid.
     */
    public function isPaid(): bool
    {
        return $this->payment_status === 'paid';
    }

    /**
     * Check if purchase has outstanding debt.
     */
    public function hasDebt(): bool
    {
        return $this->payment_status !== 'paid';
    }
}