<?php

namespace App\Models;

use App\Traits\HasSlugHistory;
use App\Traits\HasAreaStatistics;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class ParkArea extends Model
{
    use HasFactory, HasSlugHistory, HasAreaStatistics;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<string>
     */
    protected $fillable = [
        'name',
        'slug',
        'description',
        'opening_date',
        'closing_date',
        'position',
        'parent_id',
        'ride_count',
        'coaster_count',
        'flat_ride_count',
        'water_ride_count',
        'daily_capacity',
        'peak_wait_time',
        'average_rating',
        'total_rides_operated',
        'retired_rides_count',
        'last_new_ride_added',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'opening_date' => 'date',
        'closing_date' => 'date',
        'position' => 'integer',
        'ride_count' => 'integer',
        'coaster_count' => 'integer',
        'flat_ride_count' => 'integer',
        'water_ride_count' => 'integer',
        'daily_capacity' => 'integer',
        'peak_wait_time' => 'integer',
        'average_rating' => 'decimal:2',
        'total_rides_operated' => 'integer',
        'retired_rides_count' => 'integer',
        'last_new_ride_added' => 'date',
    ];

    /**
     * Get the park that owns the area.
     */
    public function park(): BelongsTo
    {
        return $this->belongsTo(Park::class);
    }

    /**
     * Get the parent area if this is a sub-area.
     */
    public function parent(): BelongsTo
    {
        return $this->belongsTo(ParkArea::class, 'parent_id');
    }

    /**
     * Get the sub-areas of this area.
     */
    public function children(): HasMany
    {
        return $this->hasMany(ParkArea::class, 'parent_id')
            ->orderBy('position');
    }

    /**
     * Get a brief description suitable for cards and previews.
     */
    public function getBriefDescriptionAttribute(): string
    {
        $description = $this->description ?? '';
        return strlen($description) > 150 ? substr($description, 0, 150) . '...' : $description;
    }

    /**
     * Get the opening year of the area.
     */
    public function getOpeningYearAttribute(): ?string
    {
        return $this->opening_date?->format('Y');
    }

    /**
     * Check if the area is currently operating.
     */
    public function isOperating(): bool
    {
        if ($this->closing_date) {
            return false;
        }

        return true;
    }

    /**
     * Check if this area has sub-areas.
     */
    public function hasChildren(): bool
    {
        return $this->children()->exists();
    }

    /**
     * Check if this is a top-level area.
     */
    public function isTopLevel(): bool
    {
        return is_null($this->parent_id);
    }

    /**
     * Get the next available position for a new area.
     */
    public function getNextPosition(): int
    {
        $maxPosition = static::where('park_id', $this->park_id)
            ->where('parent_id', $this->parent_id)
            ->max('position');

        return ($maxPosition ?? -1) + 1;
    }

    /**
     * Move this area to a new position.
     */
    public function moveToPosition(int $newPosition): void
    {
        if ($newPosition === $this->position) {
            return;
        }

        $oldPosition = $this->position;

        if ($newPosition > $oldPosition) {
            // Moving down: decrement positions of items in between
            static::where('park_id', $this->park_id)
                ->where('parent_id', $this->parent_id)
                ->whereBetween('position', [$oldPosition + 1, $newPosition])
                ->decrement('position');
        } else {
            // Moving up: increment positions of items in between
            static::where('park_id', $this->park_id)
                ->where('parent_id', $this->parent_id)
                ->whereBetween('position', [$newPosition, $oldPosition - 1])
                ->increment('position');
        }

        $this->update(['position' => $newPosition]);
    }

    /**
     * Scope query to only include operating areas.
     */
    public function scopeOperating($query)
    {
        return $query->whereNull('closing_date');
    }

    /**
     * Scope query to only include top-level areas.
     */
    public function scopeTopLevel($query)
    {
        return $query->whereNull('parent_id');
    }

    /**
     * Override parent method to ensure unique slugs within a park.
     */
    protected function generateSlug(): string
    {
        $slug = \Str::slug($this->name);
        $count = 2;

        while (
            static::where('park_id', $this->park_id)
                ->where('slug', $slug)
                ->where('id', '!=', $this->id)
                ->exists() ||
            static::whereHas('slugHistories', function ($query) use ($slug) {
                $query->where('slug', $slug);
            })
                ->where('park_id', $this->park_id)
                ->where('id', '!=', $this->id)
                ->exists()
        ) {
            $slug = \Str::slug($this->name) . '-' . $count++;
        }

        return $slug;
    }

    /**
     * Get the route key for the model.
     */
    public function getRouteKeyName(): string
    {
        return 'slug';
    }

    /**
     * Find an area by its slug within a specific park.
     */
    public static function findByParkAndSlug(Park $park, string $slug): ?self
    {
        // Try current slug
        $area = static::where('park_id', $park->id)
            ->where('slug', $slug)
            ->first();

        if ($area) {
            return $area;
        }

        // Try historical slug
        $slugHistory = SlugHistory::where('slug', $slug)
            ->where('sluggable_type', static::class)
            ->whereHas('sluggable', function ($query) use ($park) {
                $query->where('park_id', $park->id);
            })
            ->latest()
            ->first();

        return $slugHistory ? static::find($slugHistory->sluggable_id) : null;
    }

    /**
     * Boot the model.
     */
    protected static function boot()
    {
        parent::boot();

        static::creating(function (ParkArea $area) {
            if (is_null($area->position)) {
                $area->position = $area->getNextPosition();
            }
        });
    }
}