<?php

/** @noinspection PhpUnhandledExceptionInspection */

namespace DefStudio\GameEngine\Models\Concerns;

use Closure;
use ReflectionClass;
use ReflectionMethod;
use Illuminate\Database\Eloquent\Model;
use DefStudio\GameEngine\Attributes\ParentScope;
use DefStudio\GameEngine\Exceptions\ScopeException;
use DefStudio\GameEngine\Models\Contracts\FitsInScope;

trait BelongsToScope
{
    public static function get_parent_scope_relationship(): string
    {
        $reflection_class = new ReflectionClass(self::class);

        $parent_scope_relationships = collect($reflection_class->getMethods())
            ->filter(fn(ReflectionMethod $method) => !empty($method->getAttributes(ParentScope::class)));

        throw_if($parent_scope_relationships->isEmpty(), ScopeException::no_scope_detected(self::class));
        throw_if($parent_scope_relationships->count() > 1, ScopeException::multiple_scope_detected(self::class));

        return $parent_scope_relationships->first()->name;
    }

    public static function get_parent_scope_relationship_chain(): string
    {
        $parent_scope = self::get_parent_scope_relationship();

        $reflection_method = new ReflectionMethod(self::class, $parent_scope);
        $attribute = $reflection_method->getAttributes(ParentScope::class)[0];

        /** @var FitsInScope $parent_class */
        $parent_class = $attribute->getArguments()[0];

        if (!collect(class_implements($parent_class))->has(FitsInScope::class)) {
            return $parent_scope;
        }

        return $parent_class::get_parent_scope_relationship_chain().'.'.$parent_scope;
    }

    public function load_scopes(): void
    {
        $this->load(self::get_parent_scope_relationship_chain());
    }

    public function parent_scope(): Model
    {
        return $this->getAttribute(self::get_parent_scope_relationship());
    }

    public function traverse_parent_scopes(Closure $callback): void
    {
        $parent_scope = $this->parent_scope();

        $callback($parent_scope);

        if ($parent_scope instanceof FitsInScope) {
            $parent_scope->traverse_parent_scopes($callback);
        }
    }
}
