<?php

/** @noinspection PhpUnhandledExceptionInspection */

namespace DefStudio\GameEngine\Models\Concerns;

use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Collection;

trait CanCheckRelationships
{
    public function has_attached(Model $model, string $relationship_name = null): bool
    {
        $relationship_name ??= $this->guess_relationship_name($model);

        return $this->$relationship_name->contains(fn(Model $relationship_model) => $relationship_model->getAttribute($relationship_model->getKeyName()) === $model->getAttribute($model->getKeyName()));
    }

    /**
     * @param  Collection<int, Model>  $models
     */
    public function has_some_attached(Collection $models, string $relationship_name = null): bool
    {
        return $models->some(fn(Model $model) => $this->has_attached($model, $relationship_name));
    }

    /**
     * @param  Collection<int, Model>  $models
     */
    public function has_all_attached(Collection $models, string $relationship_name = null): bool
    {
        return $models->every(fn(Model $model) => $this->has_attached($model, $relationship_name));
    }

    /**
     * @param  Model|Collection<int, Model>  $model
     */
    private function guess_relationship_name(Model|Collection $model): string
    {
        $original_model = $model;

        do {
            $model = $model instanceof Model ? $model : $model->first();
            $model_class_basename = class_basename($model);
            $snake_class_basename = str($model_class_basename)->snake();
            $guessed = $snake_class_basename->plural()->toString();

            if (method_exists($this, $guessed)) {
                return $guessed;
            }
        } while ($model = get_parent_class($model));

        throw new Exception('Cannot guess relationship name from model [%s] in class [%s]', $original_model::class, $this::class);
    }
}
