<?php

namespace App\Domains\Event\Models;

use App\Domains\Catalog\Models\Variant;
use App\Domains\Event\Services\EventDateTextFormatter;
use App\Domains\Tenant\Models\Tenant;
use App\Domains\Ticket\Enums\ValidityTimeType;
use App\Domains\Ticket\Models\ValidityTime;
use Carbon\CarbonInterface;
use Illuminate\Database\Eloquent\Attributes\Fillable;
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\Support\Carbon;

#[Fillable([
    'tenant_code',
    'date',
    'time_start',
    'time_end',
])]
class EventDate extends Model
{
    use HasFactory;

    public $timestamps = false;

    protected static function booted(): void
    {
        static::creating(fn (self $eventDate) => $eventDate->syncValidityTime());
        static::created(fn (self $eventDate) => $eventDate->syncTenantDateText());
        static::updated(function (self $eventDate): void {
            if ($eventDate->wasChanged(['date', 'time_start', 'time_end'])) {
                $eventDate->syncValidityTime();
            }

            $eventDate->syncTenantDateText();
        });
        static::deleted(function (self $eventDate): void {
            $eventDate->syncTenantDateText();
            ValidityTime::query()
                ->whereKey($eventDate->validity_time_id)
                ->delete();
        });
    }

    protected function casts(): array
    {
        return [
            'date' => 'date:Y-m-d',
            'validity_time_id' => 'integer',
        ];
    }

    /** @return BelongsTo<Tenant, $this> */
    public function tenant(): BelongsTo
    {
        return $this->belongsTo(Tenant::class, 'tenant_code', 'codigo');
    }

    /** @return BelongsTo<ValidityTime, $this> */
    public function validityTime(): BelongsTo
    {
        return $this->belongsTo(ValidityTime::class);
    }

    /** @return HasMany<Variant, $this> */
    public function variants(): HasMany
    {
        return $this->hasMany(Variant::class);
    }

    /** @return BelongsToMany<Variant, $this> */
    public function selectedByVariants(): BelongsToMany
    {
        return $this->belongsToMany(
            Variant::class,
            'variant_event_dates',
            'event_date_id',
            'variant_id',
        );
    }

    public function startsAt(): CarbonInterface
    {
        return Carbon::parse($this->date->format('Y-m-d').' '.$this->time_start);
    }

    public function endsAt(): CarbonInterface
    {
        return Carbon::parse($this->date->format('Y-m-d').' '.$this->time_end);
    }

    private function syncTenantDateText(): void
    {
        $tenant = $this->tenant()->first();

        if (! $tenant) {
            return;
        }

        $tenant->update([
            'event_date_text' => app(EventDateTextFormatter::class)->format(
                $tenant->eventDates()->pluck('date')
            ),
        ]);
    }

    private function syncValidityTime(): void
    {
        $startsAt = $this->startsAt();
        $expiresAt = $this->endsAt();

        if ($expiresAt->lessThanOrEqualTo($startsAt)) {
            $expiresAt = $expiresAt->addDay();
        }

        $attributes = [
            'type' => ValidityTimeType::FixedWindow,
            'start_time' => null,
            'end_time' => null,
            'fixed_starts_at' => $startsAt,
            'fixed_expires_at' => $expiresAt,
        ];

        if ($this->validity_time_id === null) {
            $validityTime = ValidityTime::query()->create($attributes);
            $this->validity_time_id = $validityTime->getKey();
            $this->setRelation('validityTime', $validityTime);

            return;
        }

        $this->validityTime()->update($attributes);
        $this->unsetRelation('validityTime');
    }
}
