<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Validator;

use App\Models\Page;

class Permission extends Model
{
    public function accessable()
    {
        return $this->morphTo();
    }

    public function objectable()
    {
        return $this->morphTo();
    }

    public function savePermission($action, $objectable, $accessable)
    {
        // the user/role is the accessable
        // the page/blog/content_element/livestream is the objectable

        $permission = new Permission();
        $permission->objectable_id = $objectable->id;
        $permission->objectable_type = get_class($objectable);
        $permission->action = $action;

        if (!$accessable->permissions()
                ->where('objectable_type', get_class($objectable))
                ->where('objectable_id', $objectable->id)
                ->where('action', $action)
                ->count()
        ) {
            $accessable->permissions()->save($permission);
        }

        $this->clearCache($objectable);
        cache()->tags([cache_name($accessable)])->flush();
        $objectable->refresh();
        $accessable->refresh();

        return $permission;
    }

    public function removePermission($action, $objectable, $accessable)
    {
        $accessable->permissions()
                ->where('objectable_type', get_class($objectable))
                ->where('objectable_id', $objectable->id)
                ->where('action', $action)
                ->delete();

        $this->clearCache($objectable);
        cache()->tags([cache_name($accessable)])->flush();
        $objectable->refresh();
        $accessable->refresh();

        return $accessable;
    }

    public static function findObjectable($input)
    {
        if (Str::contains(Arr::get($input, 'objectable_type'), 'App\\Models\\')) {
            $class_name = Arr::get($input, 'objectable_type');
        } else {
            $class_name = 'App\\Models\\'.Str::studly(Arr::get($input, 'objectable_type'));
        }

        Validator::make($input, [
            'objectable_id' => ['required', function ($attribute, $value, $fail) use ($input, $class_name) {
                $id_check = resolve($class_name)->find($value);
                if (!$id_check) {
                    $fail('No related object found when saving the content element');
                }
            }],
            'objectable_type' => ['required', function ($attribute, $value, $fail) use ($input, $class_name) {
                $class = resolve($class_name);
                if (!$class) {
                    $fail('No related class found when saving the content element');
                }
            }],
        ])->validate();

        return (new $class_name())->findOrFail(Arr::get($input, 'objectable_id'));
    }

    protected function clearCache($objectable)
    {
        cache()->tags([cache_name($objectable)])->flush();

        if ($objectable instanceof Page) {
            $parent_page_id = $objectable->parent_page_id;
            while ($parent_page_id > 0) {
                $parent_page = Page::find($parent_page_id);
                cache()->tags([cache_name($parent_page)])->flush();
                $parent_page->refresh();
                $parent_page_id = $parent_page->parent_page_id;
            }
        }
    }
}
