<?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\Support\Str;

class Invoice extends Model
{
    protected $fillable = [
        'company_id', 'customer_id', 'invoice_number', 'invoice_prefix', 'invoice_seq',
        'invoice_date', 'due_date', 'po_no', 'po_date', 'payment_mode', 'payment_terms', 'sales_person',
        'type', 'status', 'place_of_supply', 'is_igst', 'tax_on',
        'excise_percent', 'excise_amount',
        'subtotal', 'discount_amount', 'discount_on_total',
        'discount_all_percent', 'discount_all_type', 'discount_all_value',
        'shipping_charges', 'shipping_mode', 'custom_amount', 'custom_amount_label', 'advance_payment',
        'advance_payment_method', 'advance_payment_date', 'advance_payment_reference', 'advance_payment_memo',
        'has_shipping', 'has_discount_total', 'has_discount_all', 'has_custom_amount',
        'has_advance_payment', 'has_transport_eway', 'transport_eway',
        'taxable_amount', 'cgst_amount', 'sgst_amount', 'igst_amount', 'total_tax',
        'round_off', 'grand_total', 'paid_amount', 'payment_date',
        'notes', 'terms', 'private_notes', 'einvoice_irn', 'eway_bill_no',
        'share_token',
    ];

    protected $casts = [
        'invoice_date' => 'date',
        'due_date' => 'date',
        'po_date' => 'date',
        'payment_date' => 'date',
        'advance_payment_date' => 'date',
        'is_igst' => 'boolean',
        'has_shipping' => 'boolean',
        'has_discount_total' => 'boolean',
        'has_discount_all' => 'boolean',
        'has_custom_amount' => 'boolean',
        'has_advance_payment' => 'boolean',
        'has_transport_eway' => 'boolean',
        'grand_total' => 'decimal:2',
        'paid_amount' => 'decimal:2',
        'total_tax' => 'decimal:2',
        'excise_percent' => 'decimal:2',
        'excise_amount' => 'decimal:2',
    ];

    protected $appends = ['balance', 'display_status', 'dr_cr'];

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

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

    public function getBalanceAttribute(): float
    {
        return max(0, (float) $this->grand_total - (float) $this->paid_amount);
    }

    public function getDrCrAttribute(): string
    {
        if ($this->balance <= 0 && $this->paid_amount > $this->grand_total) {
            return 'Cr';
        }

        return $this->balance > 0 ? 'Dr' : '—';
    }

    public function getDisplayStatusAttribute(): string
    {
        if ($this->status === 'cancelled') {
            return 'cancelled';
        }

        if (in_array($this->status, ['sent', 'unpaid', 'partial']) && $this->isOverdue()) {
            return 'overdue';
        }

        if ($this->status === 'sent' && $this->balance > 0 && $this->paid_amount <= 0) {
            return 'unpaid';
        }

        if ($this->balance <= 0 && $this->paid_amount > 0 && $this->grand_total > 0) {
            return 'paid';
        }

        if ($this->paid_amount > 0 && $this->balance > 0) {
            return 'partial';
        }

        return $this->status;
    }

    public function isOverdue(): bool
    {
        return $this->due_date
            && $this->due_date->isPast()
            && $this->balance > 0
            && ! in_array($this->status, ['paid', 'cancelled', 'draft']);
    }

    public function typeLabel(): string
    {
        return config('invoice.types.'.$this->type, ucfirst(str_replace('_', ' ', $this->type)));
    }

    public function statusMeta(): array
    {
        $key = $this->display_status;

        return config('invoice.statuses.'.$key, config('invoice.statuses.draft'));
    }

    public function scopeForCompany(Builder $query, int $companyId): Builder
    {
        return $query->where('company_id', $companyId);
    }

    public function isExcise(): bool
    {
        return $this->type === 'excise_invoice';
    }

    public function ensureShareToken(): string
    {
        if ($this->share_token) {
            return $this->share_token;
        }

        do {
            $token = Str::lower(Str::random(40));
        } while (static::where('share_token', $token)->exists());

        $this->forceFill(['share_token' => $token])->save();

        return $token;
    }

    public function publicShareUrl(): string
    {
        return route('public.invoices.show', $this->ensureShareToken());
    }
}
