<?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\MorphMany;

class ServiceOrder extends Model
{
    protected $guarded = [];

    protected function casts(): array
    {
        return [
            'is_express' => 'boolean',
            'price_total' => 'decimal:2',
            'paid_at' => 'immutable_datetime',
            'due_at' => 'immutable_datetime',
            'delivered_at' => 'immutable_datetime',
        ];
    }

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

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

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

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

    public function uploads(): MorphMany
    {
        return $this->morphMany(Upload::class, 'uploadable');
    }

    public function sourceFiles(): MorphMany
    {
        return $this->uploads()->where('kind', 'source_audio');
    }

    public function deliveries(): MorphMany
    {
        return $this->uploads()->where('kind', 'delivered_master');
    }

    /** Orders the studio still owes work on. */
    public function scopeOutstanding(Builder $query): Builder
    {
        return $query->whereIn('status', ['in_progress', 'awaiting_files']);
    }

    public function isPaidByPackage(): bool
    {
        return $this->package_purchase_id !== null;
    }

    public function isOverdue(?CarbonImmutable $now = null): bool
    {
        if ($this->due_at === null || $this->delivered_at !== null) {
            return false;
        }

        return $this->due_at->lessThan($now ?? CarbonImmutable::now('UTC'));
    }

    /**
     * The studio cannot start until it has something to work on. An order
     * paid for but with no audio attached is waiting on the customer, not on
     * the engineers — and the two must not look the same in the admin panel.
     */
    public function hasSource(): bool
    {
        return $this->sourceFiles()->exists();
    }
}
