<?php

namespace App\Domain\Event\Models;

use App\Domain\Event\Enums\EventStatus;
use App\Domain\Event\Enums\EventType;
use App\Domain\Event\Enums\LocationType;
use App\Domain\Facility\Models\Facility;
use App\Domain\Shared\Traits\BelongsToAcademy;
use App\Domain\Shared\Traits\HasUuid;
use App\Domain\Website\Models\Media;
use App\Models\User;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphOne;
use Illuminate\Database\Eloquent\SoftDeletes;

class Event extends Model
{
    use BelongsToAcademy, HasUuid, SoftDeletes;

    /**
     * Deliberately not branch-scoped.
     *
     * An event is an academy-wide occasion — a tournament, an open day, a trial
     * week — and is one of the few operational things that genuinely belongs to
     * the academy rather than to one of its branches. The `branch_id` column
     * survives from the first isolation pass; dropping it would be destructive
     * and a per-branch event is plausible later, but nothing reads it today.
     *
     * Named rather than merely absent so that BranchValidationRulesTest can
     * tell a decision from an oversight.
     */
    public const BRANCH_SCOPE_EXEMPT = 'academy-wide by design; see docs/agent-rules/19';

    protected $fillable = [
        'academy_id', 'branch_id',
        'title',
        'title_en',
        'slug',
        'description',
        'description_en',
        'type',
        'status',
        'location_type',
        'facility_id',
        'location_name',
        'location_address',
        'map_locations',
        'starts_at',
        'ends_at',
        'registration_opens_at',
        'registration_closes_at',
        'max_capacity',
        'form_fields',
        'settings',
        'registrations_count',
        'published_at',
        'created_by',
    ];

    protected $casts = [
        'type' => EventType::class,
        'status' => EventStatus::class,
        'location_type' => LocationType::class,
        'starts_at' => 'datetime',
        'ends_at' => 'datetime',
        'registration_opens_at' => 'datetime',
        'registration_closes_at' => 'datetime',
        'published_at' => 'datetime',
        'max_capacity' => 'integer',
        'registrations_count' => 'integer',
        'form_fields' => 'array',
        'settings' => 'array',
        'map_locations' => 'array',
    ];

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

    public function creator(): BelongsTo
    {
        return $this->belongsTo(User::class, 'created_by');
    }

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

    public function cover(): MorphOne
    {
        return $this->morphOne(Media::class, 'mediable')->where('collection', 'event_cover');
    }

    public function gallery(): MorphMany
    {
        return $this->morphMany(Media::class, 'mediable')->where('collection', 'event_gallery')->orderBy('sort_order');
    }

    public function scopePublished($query)
    {
        return $query->whereNotIn('status', ['draft', 'cancelled']);
    }

    public function scopeUpcoming($query)
    {
        return $query->where('starts_at', '>', now());
    }

    public function scopeRegistrationOpen($query)
    {
        return $query->where('status', 'published')
            ->where(function ($q) {
                $q->whereNull('registration_opens_at')->orWhere('registration_opens_at', '<=', now());
            })
            ->where(function ($q) {
                $q->whereNull('registration_closes_at')->orWhere('registration_closes_at', '>=', now());
            });
    }

    public function isRegistrationOpen(): bool
    {
        if (!in_array($this->status, [EventStatus::Published, EventStatus::InProgress])) {
            return false;
        }

        if ($this->registration_opens_at && now()->lt($this->registration_opens_at)) {
            return false;
        }

        if ($this->registration_closes_at && now()->gt($this->registration_closes_at)) {
            return false;
        }

        return $this->hasCapacity();
    }

    public function hasCapacity(): bool
    {
        if ($this->max_capacity === null) {
            return true;
        }

        return $this->registrations_count < $this->max_capacity;
    }

    public function spotsRemaining(): ?int
    {
        if ($this->max_capacity === null) {
            return null;
        }

        return max(0, $this->max_capacity - $this->registrations_count);
    }

    public function getPublicUrl(): string
    {
        $academy = $this->academy ?? app('current_academy');

        return url("/site/{$academy->slug}/events/{$this->slug}");
    }
}
