<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;

class Invoice extends Model
{
    use HasFactory;

    protected $fillable = [
        'invoice_number',
        'status',
        'customer_id',
        'service_order_id',
        'job_card_id',
        'estimate_id',
        'branch_id',
        'created_by',
        'invoice_date',
        'due_date',
        'description',
        'notes',
        'terms_and_conditions',
        'subtotal',
        'tax_rate',
        'tax_amount',
        'discount_amount',
        'total_amount',
        'paid_at',
        'payment_method',
        'payment_reference',
        'payment_notes',
        'sent_at',
        'sent_method',
        'sent_to',
    ];

    protected $casts = [
        'invoice_date' => 'date',
        'due_date' => 'date',
        'paid_at' => 'datetime',
        'sent_at' => 'datetime',
        'subtotal' => 'decimal:2',
        'tax_rate' => 'decimal:2',
        'tax_amount' => 'decimal:2',
        'discount_amount' => 'decimal:2',
        'total_amount' => 'decimal:2',
    ];

    /**
     * Generate the next invoice number for a branch
     */
    public static function generateInvoiceNumber(string $branchCode = 'MAIN'): string
    {
        $year = now()->year;
        $prefix = strtoupper($branchCode).'-INV-'.$year.'-';

        $lastInvoice = static::where('invoice_number', 'like', $prefix.'%')
            ->orderBy('invoice_number', 'desc')
            ->first();

        if ($lastInvoice) {
            $lastNumber = (int) substr($lastInvoice->invoice_number, strlen($prefix));
            $nextNumber = $lastNumber + 1;
        } else {
            $nextNumber = 1;
        }

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

    /**
     * Status options for invoices
     */
    public static function getStatusOptions(): array
    {
        return [
            'draft' => 'Draft',
            'sent' => 'Sent',
            'paid' => 'Paid',
            'overdue' => 'Overdue',
            'cancelled' => 'Cancelled',
        ];
    }

    /**
     * Payment method options
     */
    public static function getPaymentMethodOptions(): array
    {
        return [
            'cash' => 'Cash',
            'card' => 'Credit/Debit Card',
            'check' => 'Check',
            'bank_transfer' => 'Bank Transfer',
            'other' => 'Other',
        ];
    }

    /**
     * Check if invoice is overdue
     */
    public function isOverdue(): bool
    {
        return $this->status !== 'paid' &&
               $this->status !== 'cancelled' &&
               $this->due_date < now()->startOfDay();
    }

    /**
     * Check if invoice is paid
     */
    public function isPaid(): bool
    {
        return $this->status === 'paid' && ! is_null($this->paid_at);
    }

    /**
     * Mark invoice as paid
     */
    public function markAsPaid(?string $paymentMethod = null, ?string $reference = null, ?string $notes = null): void
    {
        $this->update([
            'status' => 'paid',
            'paid_at' => now(),
            'payment_method' => $paymentMethod,
            'payment_reference' => $reference,
            'payment_notes' => $notes,
        ]);
    }

    /**
     * Mark invoice as sent
     */
    public function markAsSent(string $method = 'email', ?string $sentTo = null): void
    {
        $this->update([
            'status' => 'sent',
            'sent_at' => now(),
            'sent_method' => $method,
            'sent_to' => $sentTo,
        ]);
    }

    /**
     * Calculate totals from line items
     */
    public function recalculateTotals(): void
    {
        $subtotal = $this->lineItems()->sum('total_amount');
        $taxAmount = $subtotal * ($this->tax_rate / 100);
        $total = $subtotal + $taxAmount - $this->discount_amount;

        $this->update([
            'subtotal' => $subtotal,
            'tax_amount' => $taxAmount,
            'total_amount' => $total,
        ]);
    }

    // Relationships

    /**
     * Invoice belongs to a customer
     */
    public function customer(): BelongsTo
    {
        return $this->belongsTo(Customer::class);
    }

    /**
     * Invoice belongs to a branch
     */
    public function branch(): BelongsTo
    {
        return $this->belongsTo(Branch::class);
    }

    /**
     * Invoice belongs to a user (creator)
     */
    public function createdBy(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

    /**
     * Invoice may belong to a service order
     */
    public function serviceOrder(): BelongsTo
    {
        return $this->belongsTo(ServiceOrder::class);
    }

    /**
     * Invoice may belong to a job card
     */
    public function jobCard(): BelongsTo
    {
        return $this->belongsTo(JobCard::class);
    }

    /**
     * Invoice may belong to an estimate
     */
    public function estimate(): BelongsTo
    {
        return $this->belongsTo(Estimate::class);
    }

    /**
     * Invoice has many line items
     */
    public function lineItems(): HasMany
    {
        return $this->hasMany(InvoiceLineItem::class);
    }

    // Scopes

    /**
     * Scope to filter by branch
     */
    public function scopeByBranch($query, string $branchCode)
    {
        return $query->whereHas('branch', function ($q) use ($branchCode) {
            $q->where('code', $branchCode);
        });
    }

    /**
     * Scope to filter by status
     */
    public function scopeByStatus($query, string $status)
    {
        return $query->where('status', $status);
    }

    /**
     * Scope to get overdue invoices
     */
    public function scopeOverdue($query)
    {
        return $query->where('status', '!=', 'paid')
            ->where('status', '!=', 'cancelled')
            ->where('due_date', '<', now()->startOfDay());
    }

    /**
     * Scope to get paid invoices
     */
    public function scopePaid($query)
    {
        return $query->where('status', 'paid');
    }

    /**
     * Scope to get unpaid invoices
     */
    public function scopeUnpaid($query)
    {
        return $query->whereIn('status', ['draft', 'sent', 'overdue']);
    }
}
