<?php

namespace App\Models;

use App\Enums\ParkStatus;
use App\Traits\HasLocation;
use App\Models\Operator;
use App\Traits\HasSlugHistory;
use App\Traits\HasParkStatistics;
use App\Traits\TrackedModel;
use Illuminate\Support\Facades\DB;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Factories\HasFactory;

class Park extends Model
{
    use HasFactory, HasSlugHistory, HasParkStatistics, HasLocation;

    /**
     * The attributes that are mass assignable.
     *
     * @var array<string>
     */
    protected $fillable = [
        'name',
        'slug',
        'description',
        'status',
        'opening_date',
        'closing_date',
        'operating_season',
        'size_acres',
        'website',
        'operator_id',
        'total_areas',
        'operating_areas',
        'closed_areas',
        'total_rides',
        'total_coasters',
        'total_flat_rides',
        'total_water_rides',
        'total_daily_capacity',
        'average_wait_time',
        'average_rating',
        'total_rides_operated',
        'total_rides_retired',
        'last_expansion_date',
        'last_major_update',
        'utilization_rate',
        'peak_daily_attendance',
        'guest_satisfaction',
    ];

    /**
     * The attributes that should be cast.
     *
     * @var array<string, string>
     */
    protected $casts = [
        'status' => ParkStatus::class,
        'opening_date' => 'date',
        'closing_date' => 'date',
        'size_acres' => 'decimal:2',
        'total_areas' => 'integer',
        'operating_areas' => 'integer',
        'closed_areas' => 'integer',
        'total_rides' => 'integer',
        'total_coasters' => 'integer',
        'total_flat_rides' => 'integer',
        'total_water_rides' => 'integer',
        'total_daily_capacity' => 'integer',
        'average_wait_time' => 'integer',
        'average_rating' => 'decimal:2',
        'total_rides_operated' => 'integer',
        'total_rides_retired' => 'integer',
        'last_expansion_date' => 'date',
        'last_major_update' => 'date',
        'utilization_rate' => 'decimal:2',
        'peak_daily_attendance' => 'integer',
        'guest_satisfaction' => 'decimal:2',
    ];

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

    /**
     * Get the owner that owns the park.
     * @deprecated Use operator() relationship instead until Company model is implemented
     */
    public function owner(): BelongsTo
    {
        return $this->belongsTo(Operator::class, 'owner_id');
    }

    /**
     * Get the areas in the park.
     */
    public function areas(): HasMany
    {
        return $this->hasMany(ParkArea::class);
    }

    /**
     * Get the photos for the park.
     */
    public function photos(): MorphMany
    {
        return $this->morphMany(Photo::class, 'photoable');
    }

    /**
     * Get the featured photo for the park.
     */
    public function featuredPhoto()
    {
        return $this->photos()->where('is_featured', true)->first()
            ?? $this->photos()->orderBy('position')->first();
    }

    /**
     * Get the URL of the featured photo or a default image.
     */
    public function getFeaturedPhotoUrlAttribute(): string
    {
        $photo = $this->featuredPhoto();
        return $photo ? $photo->url : asset('images/placeholders/default-park.jpg');
    }

    /**
     * Add a photo to the park.
     *
     * @param array<string, mixed> $attributes
     * @return \App\Models\Photo
     */
    public function addPhoto(array $attributes): Photo
    {
        // Set position to be the last in the collection if not specified
        if (!isset($attributes['position'])) {
            $lastPosition = $this->photos()->max('position') ?? 0;
            $attributes['position'] = $lastPosition + 1;
        }

        // If this is the first photo or is_featured is true, make it featured
        if ($this->photos()->count() === 0 || ($attributes['is_featured'] ?? false)) {
            $attributes['is_featured'] = true;
        } else {
            $attributes['is_featured'] = false;
        }

        return $this->photos()->create($attributes);
    }

    /**
     * Set a photo as the featured photo.
     *
     * @param \App\Models\Photo|int $photo
     * @return bool
     */
    public function setFeaturedPhoto($photo): bool
    {
        if (is_numeric($photo)) {
            $photo = $this->photos()->findOrFail($photo);
        }

        return $photo->setAsFeatured();
    }

    /**
     * Reorder photos.
     *
     * @param array<int, int> $photoIds Ordered array of photo IDs
     * @return bool
     */
    public function reorderPhotos(array $photoIds): bool
    {
        // Begin transaction
        DB::beginTransaction();

        try {
            foreach ($photoIds as $position => $photoId) {
                $this->photos()->where('id', $photoId)->update(['position' => $position + 1]);
            }

            DB::commit();
            return true;
        } catch (\Exception $e) {
            DB::rollBack();
            return false;
        }
    }

    /**
     * Get formatted website URL (ensures proper URL format).
     */
    public function getWebsiteUrlAttribute(): string
    {
        if (!$this->website) {
            return '';
        }

        $website = $this->website;
        if (!str_starts_with($website, 'http://') && !str_starts_with($website, 'https://')) {
            $website = 'https://' . $website;
        }

        return $website;
    }

    /**
     * Get the status display classes for Tailwind CSS.
     */
    public function getStatusClassesAttribute(): string
    {
        return $this->status->getStatusClasses();
    }

    /**
     * Get a formatted display of the park's size.
     */
    public function getSizeDisplayAttribute(): string
    {
        return $this->size_acres ? number_format($this->size_acres, 1) . ' acres' : 'Unknown size';
    }

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

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

    /**
     * Scope a query to only include operating parks.
     */
    public function scopeOperating($query)
    {
        return $query->where('status', ParkStatus::OPERATING);
    }

    /**
     * Scope a query to only include closed parks.
     */
    public function scopeClosed($query)
    {
        return $query->whereIn('status', [
            ParkStatus::CLOSED_TEMP,
            ParkStatus::CLOSED_PERM,
            ParkStatus::DEMOLISHED,
        ]);
    }

    /**
     * Get the formatted location for the park.
     */
    public function getFormattedLocationAttribute(): string
    {
        return $this->formatted_address ?? '';
    }

    /**
     * Get the absolute URL for the park detail page.
     */
    public function getAbsoluteUrl(): string
    {
        return route('parks.show', ['slug' => $this->slug]);
    }

    /**
     * Get a park by its current or historical slug.
     *
     * @param string $slug
     * @return array{0: \App\Models\Park|null, 1: bool} Park and whether a historical slug was used
     */
    public static function getBySlug(string $slug): array
    {
        // Try current slug
        $park = static::where('slug', $slug)->first();
        if ($park) {
            return [$park, false];
        }

        // Try historical slug
        $slugHistory = SlugHistory::where('slug', $slug)
            ->where('sluggable_type', static::class)
            ->latest()
            ->first();

        if ($slugHistory) {
            $park = static::find($slugHistory->sluggable_id);
            return [$park, true];
        }

        return [null, false];
    }

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

        static::created(function (Park $park) {
            $park->operator?->updateStatistics();
        });

        static::deleted(function (Park $park) {
            $park->operator?->updateStatistics();
        });
    }
}