<?php

namespace App\Models;

use App\Enums\InvoiceStatus;
use App\Observers\InvoiceObserver;
use Illuminate\Database\Eloquent\Attributes\ObservedBy;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Eloquent\SoftDeletes;

#[ObservedBy(InvoiceObserver::class)]

class Invoice extends Model
{
    use HasFactory, SoftDeletes;

    /**
     * @var float|int|mixed
     */
    protected $fillable = [
        'internal_id',
        'customer_id',
        'status',
        'subtotal',
        'total',
        'pst_rate',
        'gst_rate',
        'hst_rate',
        'pst_amount',
        'gst_amount',
        'hst_amount',
        'has_pst',
        'has_gst',
        'has_hst',
        'date',
        'due_date',
    ];

    protected $casts = [
        'has_gst'    => 'boolean',
        'has_pst'    => 'boolean',
        'has_hst'    => 'boolean',
        'date'       => 'date',
        'due_date'   => 'date',
        'status'     => InvoiceStatus::class,
        'subtotal'   => 'float',
        'pst_amount' => 'float',
        'gst_amount' => 'float',
        'hst_amount' => 'float',
        'total'      => 'float',
    ];

    public function remainingBalance(): float
    {
        $applied = $this->payments()->sum('applied_amount');

        return max(0, $this->total - $applied);
    }

    public function calculateTotals(): void
    {
        $this->refresh();
        $this->loadMissing('orders');

        if ($this->orders->isEmpty()) {

            $this->subtotal   = 0;
            $this->gst_amount = 0;
            $this->pst_amount = 0;
            $this->hst_amount = 0;
            $this->total      = 0;

            return;
        }

        $subtotal = $this->orders->sum(fn (Order $order) => $order->total_service_price);

        $this->subtotal = $subtotal;
        $this->saveQuietly();

        $gstAmount = $this->calculateTaxAmount($subtotal, $this->gst_rate, $this->has_gst);
        $pstAmount = $this->calculateTaxAmount($subtotal, $this->pst_rate, $this->has_pst);
        $hstAmount = $this->calculateTaxAmount($subtotal, $this->hst_rate, $this->has_hst);

        $this->gst_amount = $gstAmount;
        $this->pst_amount = $pstAmount;
        $this->hst_amount = $hstAmount;

        $this->total = $subtotal + $gstAmount + $pstAmount + $hstAmount;

        $this->saveQuietly();
    }

    private function calculateTaxAmount(float $amount, float $rate, ?bool $apply): float
    {
        return $apply ? $amount * ($rate / 100) : 0;
    }

    public function setStatus(InvoiceStatus $status): void
    {
        if ($this->status !== $status) {
            $this->status = $status;
            $this->save();
        }
    }

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

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

    public function productServices(): HasManyThrough
    {
        return $this->hasManyThrough(ProductService::class, Order::class);
    }

    public function invoiceReports(): BelongsToMany
    {
        return $this->belongsToMany(InvoiceReport::class);
    }

    public function payments(): BelongsToMany
    {
        return $this->belongsToMany(Payment::class)
            ->withPivot('applied_amount')
            ->withTimestamps();
    }
}
