<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;

/**
 * A purchase from a supplier — header row + items + payments.
 *
 * Status lifecycle (see `docs/features/suppliers-purchases.md` §2):
 *   draft → submitted → received → partially_paid → paid
 *
 * This slice (Slice 2) covers the **draft only** — Save & Receive,
 * journal posting, stock movements, and payments land in later slices.
 *
 * Totals on this row are server-computed from line items at save time
 * (see {@see App\Actions\Purchases\CreatePurchase}). `paid_total` and
 * `balance_due` stay at 0 until payments arrive. `is_received_in_full`
 * stays false until the receive flow lands.
 */
class Purchase extends Model
{
    use SoftDeletes;

    public const STATUS_DRAFT          = 'draft';
    public const STATUS_SUBMITTED      = 'submitted';
    public const STATUS_RECEIVED       = 'received';
    public const STATUS_PARTIALLY_PAID = 'partially_paid';
    public const STATUS_PAID           = 'paid';
    public const STATUS_CANCELLED      = 'cancelled';

    /**
     * Mass-assignable columns. Deliberately omits:
     *   - `number`                        — server-generated by GeneratePurchaseNumber
     *   - `status`, `is_received_in_full` — owned by transition actions only
     *   - `subtotal`, `discount_total`, `tax_total`, `grand_total`,
     *     `additional_charges_total`     — computed by ComputePurchaseTotals
     *   - `paid_total`, `balance_due`     — owned by Receive / Payment / Return actions
     *
     * These columns ARE writable, but only via `$model->forceFill(...)`
     * inside the relevant Action. Form-driven mass assignment via
     * `Purchase::create($request->all())` or a plugin filter that injects
     * them would otherwise let a user submit `status=paid` and skip the
     * receive+payment workflow entirely.
     */
    protected $fillable = [
        'store_id', 'supplier_id',
        'supplier_invoice_number',
        'purchase_date', 'due_date',
        'currency_code', 'exchange_rate_to_base',
        'client_uuid', 'notes',
        'created_by', 'updated_by',
    ];

    protected function casts(): array
    {
        return [
            'purchase_date'            => 'date',
            'due_date'                 => 'date',
            'exchange_rate_to_base'    => 'decimal:10',
            'subtotal'                 => 'decimal:4',
            'discount_total'           => 'decimal:4',
            'tax_total'                => 'decimal:4',
            'additional_charges_total' => 'decimal:4',
            'grand_total'              => 'decimal:4',
            'paid_total'               => 'decimal:4',
            'balance_due'              => 'decimal:4',
            'is_received_in_full'      => 'boolean',
            'markup_applied'           => 'boolean',
        ];
    }

    /** @param Builder<Purchase> $q */
    public function scopeDraft(Builder $q): void { $q->where('status', self::STATUS_DRAFT); }

    /** @return BelongsTo<Supplier, $this> */
    public function supplier(): BelongsTo
    {
        return $this->belongsTo(Supplier::class);
    }

    /** @return BelongsTo<Store, $this> */
    public function store(): BelongsTo
    {
        return $this->belongsTo(Store::class);
    }

    /** @return HasMany<PurchaseItem, $this> */
    public function items(): HasMany
    {
        return $this->hasMany(PurchaseItem::class)->orderBy('sort_order')->orderBy('id');
    }

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

    /** @return BelongsTo<User, $this> */
    public function updater(): BelongsTo
    {
        return $this->belongsTo(User::class, 'updated_by');
    }

    /** @return HasMany<PurchasePayment, $this> */
    public function payments(): HasMany
    {
        return $this->hasMany(PurchasePayment::class)->orderByDesc('payment_date')->orderByDesc('id');
    }

    /** @return HasMany<PurchaseReturn, $this> */
    public function returns(): HasMany
    {
        return $this->hasMany(PurchaseReturn::class)->orderByDesc('id');
    }

    /** @return \Illuminate\Database\Eloquent\Relations\MorphMany<Attachment, $this> */
    public function attachments(): \Illuminate\Database\Eloquent\Relations\MorphMany
    {
        return $this->morphMany(Attachment::class, 'attachable')->latest('id');
    }

    /** The single invoice attachment (newest), or null. */
    public function attachment(): ?Attachment
    {
        return $this->relationLoaded('attachments')
            ? $this->attachments->first()
            : $this->attachments()->first();
    }
}
