<?php

namespace App\Models\Sales;

use App\Models\Branch;
use App\Models\Concerns\Auditable;
use App\Models\Concerns\BelongsToTenant;
use App\Models\Concerns\BranchScoped;
use App\Models\Customers\Customer;
use App\Models\Customers\DeliveryAddress;
use App\Models\Lookups\Status;
use App\Models\Loyalty\LoyaltyPointTransaction;
use App\Models\People\SalesRep;
use App\Models\Vouchers\VoucherRedemption;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Str;

class Invoice extends Model
{
    use Auditable, BelongsToTenant, BranchScoped, SoftDeletes;

    protected $fillable = [
        'uuid', 'invoice_number', 'physical_reference',
        'branch_id', 'customer_id', 'delivery_address_id', 'till_id',
        'pos_session_id', 'amended_from_invoice_id', 'sales_rep_id', 'channel', 'is_legacy',
        'order_status_id', 'payment_status_id',
        'subtotal', 'vat_total', 'total', 'discount', 'cart_discount_amount', 'cart_discount_name', 'net_total',
        'invoice_payable', 'paid_amount', 'tendered_amount', 'change_amount', 'balance', 'note',
    ];

    protected function casts(): array
    {
        return [
            'is_legacy' => 'boolean',
            'subtotal' => 'decimal:2',
            'vat_total' => 'decimal:2',
            'total' => 'decimal:2',
            'discount' => 'decimal:2',
            'cart_discount_amount' => 'decimal:2',
            'net_total' => 'decimal:2',
            'invoice_payable' => 'decimal:2',
            'paid_amount' => 'decimal:2',
            'tendered_amount' => 'decimal:2',
            'change_amount' => 'decimal:2',
            'balance' => 'decimal:2',
        ];
    }

    protected static function booted(): void
    {
        static::creating(function (self $i): void {
            if (! $i->uuid) {
                $i->uuid = (string) Str::ulid();
            }
        });
    }

    public function items(): HasMany
    {
        return $this->hasMany(InvoiceItem::class);
    }

    public function claims(): HasMany
    {
        return $this->hasMany(TransactionClaim::class);
    }

    public function auditLogs(): HasMany
    {
        return $this->hasMany(InvoiceAuditLog::class);
    }

    /**
     * Voucher and loyalty ledgers are what let a print break the single
     * `discount` column back apart into the sources a customer expects to see
     * named. Read-only from the invoice's point of view.
     */
    public function voucherRedemptions(): HasMany
    {
        return $this->hasMany(VoucherRedemption::class);
    }

    public function loyaltyTransactions(): HasMany
    {
        return $this->hasMany(LoyaltyPointTransaction::class);
    }

    public function branch(): BelongsTo
    {
        return $this->belongsTo(Branch::class, 'branch_id');
    }

    public function customer(): BelongsTo
    {
        return $this->belongsTo(Customer::class);
    }

    public function deliveryAddress(): BelongsTo
    {
        return $this->belongsTo(DeliveryAddress::class);
    }

    public function till(): BelongsTo
    {
        return $this->belongsTo(Till::class);
    }

    public function posSession(): BelongsTo
    {
        return $this->belongsTo(PosSession::class);
    }

    public function amendedFrom(): BelongsTo
    {
        return $this->belongsTo(self::class, 'amended_from_invoice_id')->withTrashed();
    }

    public function rep(): BelongsTo
    {
        return $this->belongsTo(SalesRep::class, 'sales_rep_id');
    }

    public function orderStatus(): BelongsTo
    {
        return $this->belongsTo(Status::class, 'order_status_id');
    }

    public function paymentStatus(): BelongsTo
    {
        return $this->belongsTo(Status::class, 'payment_status_id');
    }

    /**
     * Fully settled: nothing left on the balance. Pending tenders (cheques)
     * never reduce the balance until they clear, so a zero balance means every
     * tender has settled — the only invoices that earn the ink PAID seal.
     */
    public function isFullyPaid(): bool
    {
        return (float) $this->balance <= 0.001;
    }
}
