<?php

namespace Modules\Payment\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Modules\Booking\Models\Booking;
use Modules\Payment\Database\Factories\PaymentFactory;
use Modules\Payment\Enums\PaymentMethod;
use Modules\Payment\Enums\PaymentStatus;
use Modules\Payment\Enums\RefundStatus;
use Spatie\Activitylog\Models\Concerns\LogsActivity;
use Spatie\Activitylog\Support\LogOptions;

/**
 * One attempt to pay for a Booking through a gateway — a Booking can have
 * more than one Payment row if an earlier attempt failed and the customer
 * retried (domain.md §1).
 */
class Payment extends Model
{
    /** @use HasFactory<PaymentFactory> */
    use HasFactory, LogsActivity;

    /**
     * Audit trail on status transitions only (domain.md §6; T6.2).
     */
    public function getActivitylogOptions(): LogOptions
    {
        return LogOptions::defaults()
            ->logOnly(['status'])
            ->logOnlyDirty()
            ->dontLogEmptyChanges()
            ->useLogName('payment');
    }

    /**
     * @var list<string>
     */
    protected $fillable = [
        'booking_id',
        'gateway',
        'status',
        'amount',
        'currency',
        'gateway_transaction_id',
        'gateway_payload',
        'initiated_at',
        'completed_at',
    ];

    /**
     * @return array<string, string>
     */
    protected function casts(): array
    {
        return [
            'gateway' => PaymentMethod::class,
            'status' => PaymentStatus::class,
            'amount' => 'decimal:2',
            'gateway_payload' => 'array',
            'initiated_at' => 'datetime',
            'completed_at' => 'datetime',
        ];
    }

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

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

    /**
     * What's left to refund on this Payment — its total minus whatever has
     * already been completed-refunded (partial refunds supported, domain.md
     * §6). Shared by RefundBookingAction's own guard and the Filament refund
     * forms, which surface it to staff before they submit.
     */
    public function refundableBalance(): string
    {
        $alreadyRefunded = (string) $this->refunds()->where('status', RefundStatus::Completed->value)->sum('amount');

        return bcsub((string) $this->amount, $alreadyRefunded, 2);
    }
}
