<?php

namespace Ignite\Core\Entities;

use Ignite\Core\Collections\GroupsCollection;

/**
 * Group Entity.
 *
 * @property int $id
 * @property string $key
 * @property string $name
 * @property int $level
 * @property string|null $description
 * @property bool|null $is_user_defined
 * @property int $status
 * @property \Illuminate\Support\Carbon|null $created_at
 * @property \Illuminate\Support\Carbon|null $updated_at
 * @property-read \Ignite\Core\Collections\PermissionsCollection|\Ignite\Core\Entities\Permission[] $hasPermissions
 * @property-read int|null $has_permissions_count
 * @property-read \Ignite\Core\Collections\PermissionsCollection|\Ignite\Core\Entities\Permission[] $permissions
 * @property-read int|null $permissions_count
 * @property-read \Illuminate\Database\Eloquent\Collection|\Ignite\Core\Entities\User[] $users
 * @property-read int|null $users_count
 * @method static GroupsCollection|static[] all($columns = ['*'])
 * @method static GroupsCollection|static[] get($columns = ['*'])
 * @method static \Illuminate\Database\Eloquent\Builder|Group newModelQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|Group newQuery()
 * @method static \Illuminate\Database\Eloquent\Builder|Group query()
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereCreatedAt($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereDescription($value)
 * @method static Builder|Base whereHasPermission(string $permission, ?\Ignite\Core\Entities\User $user = null)
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereId($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereIsUserDefined($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereKey($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereLevel($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereName($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereStatus($value)
 * @method static \Illuminate\Database\Eloquent\Builder|Group whereUpdatedAt($value)
 * @mixin \Eloquent
 */
class Group extends Base
{
    public const ACTIVE = 1;
    public const INACTIVE = 0;

    public const LEVEL_SUPER_ADMIN = 1;
    public const LEVEL_MANAGER = 2;
    public const LEVEL_SPECIALIST = 4;
    public const LEVEL_CLIENT = 8;
    public const LEVEL_PARTICIPANT = 16;

    /**
     * The custom table name.
     * @var string
     */
    protected $table = 'core_group';

    /**
     * @var array
     */
    protected $casts = [
        'is_user_defined' => 'bool',
    ];

    /**
     * Eloquent event listeners.
     */
    public static function boot()
    {
        parent::boot();
        static::saving(function (Group $model) {
            if (empty(trim($model->key))) {
                $model->key = str_slug($model->name);
            }
            if (empty(trim($model->status))) {
                $model->status = static::INACTIVE;
            }
        });
        static::saved(function () {
            app('authorization')->forgetPermissions()->registerPermissions();
        });
        static::deleting(function (Group $model) {
            if (! $model->isUserDefined()) {
                throw new \Exception('You cannot delete a system defined user group.');
            }
        });
    }

    /**
     * The users related to this group.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function users()
    {
        return $this->belongsToMany(User::class, 'core_user_group', 'group_id', 'user_id')->withTimestamps();
    }

    /**
     * The relationship to the permissions for this group.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     */
    public function permissions()
    {
        return $this->belongsToMany(Permission::class, 'core_group_permission', 'group_id', 'permission_id')->withTimestamps();
    }

    /**
     * Assign a permission to the role.
     *
     * @param Permission $permission
     * @return Model
     */
    public function givePermissionTo(Permission $permission)
    {
        $this->permissions()->save($permission);

        return $this;
    }

    /**
     * Remove the provided permission for the group.
     *
     * @param string|Permission $permission
     * @return bool
     */
    public function removePermissionFrom(Permission $permission)
    {
        $this->permissions()->detach($permission->id);

        return $this;
    }

    /**
     * Determine if the group has the provided permission.
     *
     * @param string|Permission $permission
     * @return bool
     */
    public function hasPermission($permission)
    {
        if (is_string($permission)) {
            return $this->permissions->contains('key', $permission);
        }

        if ($permission instanceof Permission) {
            return $this->permissions->contains($permission);
        }

        return false;
    }

    /**
     * Determine if the group has users.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany|Group
     * @deprecated
     */
    public function hasUsers()
    {
        return $this->users()->select('group_id');
    }

    /**
     * Determine if the group has permissions.
     *
     * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
     * @deprecated
     */
    public function hasPermissions()
    {
        return $this->permissions()->select('group_id');
    }

    /**
     * Determine if the group is user-defined.
     *
     * @return bool
     */
    public function isUserDefined()
    {
        return (bool)$this->is_user_defined;
    }

    /**
     * Determine if the group is active.
     *
     * @return bool
     */
    public function isActive()
    {
        return $this->status === static::ACTIVE;
    }

    /**
     * Determine if the group is inactive.
     *
     * @return bool
     */
    public function isInactive()
    {
        return $this->status === static::INACTIVE;
    }

    /**
     * The default system levels.
     *
     * @return array
     */
    public function getSystemLevels()
    {
        return [
            self::LEVEL_SUPER_ADMIN,
            self::LEVEL_MANAGER,
            self::LEVEL_SPECIALIST,
            self::LEVEL_CLIENT,
            self::LEVEL_PARTICIPANT,
        ];
    }

    /**
     * Determine if the instance level is a system level.
     *
     * @return bool
     */
    public function isSystemLevel()
    {
        return in_array($this->level, $this->getSystemLevels());
    }

    /**
     * Create a new Groups Collection instance.
     *
     * @param array $models
     * @return GroupsCollection
     */
    public function newCollection(array $models = [])
    {
        return new GroupsCollection($models);
    }
}
