<?php

namespace App\Traits;

use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Support\Str;
use App\Models\SlugHistory;

trait HasSlugHistory
{
    /**
     * Boot the trait.
     */
    protected static function bootHasSlugHistory(): void
    {
        static::saving(function ($model) {
            if (!$model->slug) {
                $model->slug = $model->generateSlug();
            }
        });

        static::updating(function ($model) {
            if ($model->isDirty('slug') && $model->getOriginal('slug')) {
                $model->addToSlugHistory($model->getOriginal('slug'));
            }
        });
    }

    /**
     * Get all slug histories for this model.
     */
    public function slugHistories(): MorphMany
    {
        return $this->morphMany(SlugHistory::class, 'sluggable');
    }

    /**
     * Add a slug to the history.
     */
    protected function addToSlugHistory(string $slug): void
    {
        $this->slugHistories()->create(['slug' => $slug]);
    }

    /**
     * Generate a unique slug.
     */
    protected function generateSlug(): string
    {
        $slug = Str::slug($this->name);
        $count = 2;

        while (
            static::where('slug', $slug)
                ->where('id', '!=', $this->id)
                ->exists() ||
            SlugHistory::where('slug', $slug)
                ->where('sluggable_type', get_class($this))
                ->where('sluggable_id', '!=', $this->id)
                ->exists()
        ) {
            $slug = Str::slug($this->name) . '-' . $count++;
        }

        return $slug;
    }

    /**
     * Find a model by its current or historical slug.
     *
     * @param string $slug
     * @return static|null
     */
    public static function findBySlug(string $slug)
    {
        // Try current slug
        $model = static::where('slug', $slug)->first();
        if ($model) {
            return $model;
        }

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

        if ($slugHistory) {
            return static::find($slugHistory->sluggable_id);
        }

        return null;
    }

    /**
     * Find a model by its current or historical slug or fail.
     *
     * @param string $slug
     * @return static
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
     */
    public static function findBySlugOrFail(string $slug)
    {
        return static::findBySlug($slug) ?? throw new \Illuminate\Database\Eloquent\ModelNotFoundException;
    }
}