<?php

declare(strict_types=1);

namespace App\Models;

use Carbon\CarbonImmutable;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;

/**
 * Balances are held in slot units rather than hours so that a 90 minute
 * booking deducts exactly three slots rather than rounding to one hour or two.
 */
class PackagePurchase extends Model
{
    protected $guarded = [];

    protected function casts(): array
    {
        return [
            'price_paid' => 'decimal:2',
            'skips_lead_time' => 'boolean',
            'activated_at' => 'immutable_datetime',
            'expires_at' => 'immutable_datetime',
        ];
    }

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

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

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

    public function payments(): MorphMany
    {
        return $this->morphMany(Payment::class, 'payable');
    }

    /** Paid for, in date, and with something left on it. */
    public function scopeUsable(Builder $query, ?CarbonImmutable $now = null): Builder
    {
        $now ??= CarbonImmutable::now('UTC');

        return $query
            ->where('status', 'active')
            ->where('expires_at', '>', $now)
            ->where('studio_slots_remaining', '>', 0);
    }

    public function isUsable(?CarbonImmutable $now = null): bool
    {
        $now ??= CarbonImmutable::now('UTC');

        return $this->status === 'active'
            && $this->expires_at !== null
            && $this->expires_at->greaterThan($now)
            && $this->studio_slots_remaining > 0;
    }

    public function studioHoursRemaining(): float
    {
        return $this->studio_slots_remaining * config('studio.slot_minutes') / 60;
    }

    public function engineerHoursRemaining(): float
    {
        return $this->engineer_slots_remaining * config('studio.slot_minutes') / 60;
    }

    /**
     * Whether this purchase can cover a booking of $slots length.
     *
     * Start Strong is the case that matters: ten studio hours but an engineer
     * for only five of them, so an engineer-led booking must satisfy both
     * balances while a self-serve one only touches the first.
     */
    public function canCover(int $slots, bool $needsEngineer): bool
    {
        if ($this->studio_slots_remaining < $slots) {
            return false;
        }

        return ! $needsEngineer || $this->engineer_slots_remaining >= $slots;
    }
}
