<?php

namespace App\Models;

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
{
    public const SOURCE_MANUAL = 'manual';
    public const SOURCE_SERVICE = 'service';
    public const SOURCE_SALE = 'sale';

    public const STATUS_DRAFT = 'draft';
    public const STATUS_REVIEW = 'review';
    public const STATUS_ISSUED = 'issued';
    public const STATUS_CANCELLED = 'cancelled';

    public const PAYMENT_UNPAID = 'unpaid';
    public const PAYMENT_PARTIAL = 'partial';
    public const PAYMENT_PAID = 'paid';

    public const ITEM_TYPES = ['service', 'product', 'diagnostic_fee', 'cancellation_fee', 'manual', 'other'];
    public const SOURCES = [self::SOURCE_MANUAL, self::SOURCE_SERVICE, self::SOURCE_SALE];
    public const STATUSES = [self::STATUS_DRAFT, self::STATUS_REVIEW, self::STATUS_ISSUED, self::STATUS_CANCELLED];
    public const PAYMENT_STATUSES = [self::PAYMENT_UNPAID, self::PAYMENT_PARTIAL, self::PAYMENT_PAID];

    protected $fillable = [
        'invoice_number',
        'customer_id',
        'source_type',
        'source_id',
        'status',
        'payment_status',
        'invoice_date',
        'due_date',
        'issued_at',
        'review_submitted_at',
        'review_submitted_by',
        'review_approved_at',
        'review_approved_by',
        'paid_at',
        'delivered_at',
        'delivered_by',
        'delivery_recipient',
        'delivery_note',
        'delivery_photo_path',
        'delivery_signature_path',
        'due_reminder_sent_at',
        'overdue_reminder_sent_at',
        'currency',
        'subtotal',
        'discount_total',
        'tax_total',
        'grand_total',
        'amount_paid',
        'credited_total',
        'balance_due',
        'public_note',
        'internal_note',
        'verification_code',
        'signature_path',
        'barcode_path',
        'pdf_path',
        'issued_by',
        'created_by',
        'updated_by',
        'cancelled_by',
        'cancel_reason',
    ];

    protected $casts = [
        'invoice_date' => 'date',
        'due_date' => 'date',
        'issued_at' => 'datetime',
        'review_submitted_at' => 'datetime',
        'review_approved_at' => 'datetime',
        'paid_at' => 'datetime',
        'delivered_at' => 'datetime',
        'due_reminder_sent_at' => 'datetime',
        'overdue_reminder_sent_at' => 'datetime',
        'subtotal' => 'decimal:2',
        'discount_total' => 'decimal:2',
        'tax_total' => 'decimal:2',
        'grand_total' => 'decimal:2',
        'amount_paid' => 'decimal:2',
        'credited_total' => 'decimal:2',
        'balance_due' => 'decimal:2',
    ];

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

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

    public function payments(): HasMany
    {
        return $this->hasMany(Payment::class, 'invoice_id')->latest('payment_date');
    }

    public function activities(): HasMany
    {
        return $this->hasMany(InvoiceActivityLog::class, 'invoice_id')->latest();
    }

    public function creditNotes(): HasMany
    {
        return $this->hasMany(CreditNote::class, 'invoice_id')->latest('credit_date');
    }

    public function issuer(): BelongsTo
    {
        return $this->belongsTo(User::class, 'issued_by');
    }

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

    public function editor(): BelongsTo
    {
        return $this->belongsTo(User::class, 'updated_by');
    }

    public function canceller(): BelongsTo
    {
        return $this->belongsTo(User::class, 'cancelled_by');
    }

    public function deliverer(): BelongsTo
    {
        return $this->belongsTo(User::class, 'delivered_by');
    }

    public function recalculateTotals(): void
    {
        $subtotal = $this->items->sum(fn (InvoiceItem $item) => (float) $item->qty * (float) $item->unit_price);
        $discount = $this->items->sum('discount_amount');
        $tax = $this->items->sum('tax_amount');
        $grandTotal = $this->items->sum('line_total');
        $amountPaid = $this->payments->sum('amount');
        $creditedTotal = $this->creditNotes()->sum('grand_total');
        $effectiveTotal = max($grandTotal - $creditedTotal, 0);
        $balanceDue = max($effectiveTotal - $amountPaid, 0);

        $this->forceFill([
            'subtotal' => $subtotal,
            'discount_total' => $discount,
            'tax_total' => $tax,
            'grand_total' => $grandTotal,
            'amount_paid' => $amountPaid,
            'credited_total' => $creditedTotal,
            'balance_due' => $balanceDue,
            'payment_status' => ($amountPaid + $creditedTotal) <= 0
                ? self::PAYMENT_UNPAID
                : ($balanceDue > 0 ? self::PAYMENT_PARTIAL : self::PAYMENT_PAID),
            'paid_at' => $balanceDue <= 0 && ($amountPaid + $creditedTotal) > 0 ? now() : null,
        ])->save();
    }

    public static function nextInvoiceNumber(): string
    {
        $prefix = 'INV-' . now()->format('Ym') . '-';
        $last = static::whereNotNull('invoice_number')
            ->where('invoice_number', 'like', $prefix . '%')
            ->orderByDesc('invoice_number')
            ->value('invoice_number');

        $sequence = $last ? ((int) substr($last, -5)) + 1 : 1;

        return $prefix . str_pad((string) $sequence, 5, '0', STR_PAD_LEFT);
    }

    public static function generateVerificationCode(): string
    {
        return strtoupper(Str::random(16));
    }
}