<?php
    declare(strict_types=1);

    namespace Icing\Model\Table;

    use ArrayObject;
    use Cake\Datasource\EntityInterface;
    use Cake\Datasource\ResultSetInterface;
    use Cake\Event\EventInterface;
    use Cake\ORM\Query;
    use Cake\ORM\RulesChecker;
    use Cake\Utility\Inflector;
    use Cake\Validation\Validator;
    use Closure;
    use Icing\Model\Entity\UserGroup;
    use Psr\SimpleCache\CacheInterface;

    /**
     * UserGroups Model
     *
     * @property \Icing\Model\Table\UsersTable|\Cake\ORM\Association\HasMany $Users
     * @property \Icing\Model\Table\RolesTable|\Cake\ORM\Association\BelongsToMany $Roles
     *
     * @method UserGroup newEmptyEntity()
     * @method UserGroup newEntity(array $data, array $options = [])
     * @method array<UserGroup> newEntities(array $data, array $options = [])
     * @method UserGroup get(mixed $primaryKey, array|string $finder = 'all', CacheInterface|string|null $cache = null, Closure|string|null $cacheKey = null, mixed ...$args)
     * @method UserGroup findOrCreate($search, ?callable $callback = null, array $options = [])
     * @method UserGroup patchEntity(EntityInterface $entity, array $data, array $options = [])
     * @method array<UserGroup> patchEntities(iterable $entities, array $data, array $options = [])
     * @method UserGroup|false save(EntityInterface $entity, array $options = [])
     * @method UserGroup saveOrFail(EntityInterface $entity, array $options = [])
     * @method iterable<UserGroup>|ResultSetInterface<UserGroup>|false saveMany(iterable $entities, array $options = [])
     * @method iterable<UserGroup>|ResultSetInterface<UserGroup> saveManyOrFail(iterable $entities, array $options = [])
     * @method iterable<UserGroup>|ResultSetInterface<UserGroup>|false deleteMany(iterable $entities, array $options = [])
     * @method iterable<UserGroup>|ResultSetInterface<UserGroup> deleteManyOrFail(iterable $entities, array $options = [])
     *
     * @method Query|array<UserGroup> findByDefaultUserGroup(bool $default_user_group)
     *
     * @mixin \Cake\ORM\Behavior\TimestampBehavior
     */
    class UserGroupsTable extends Table
    {
        /**
         * Initialize method
         *
         * @param array $config The configuration for the Table.
         *
         * @return void
         */
        public function initialize(array $config): void
        {
            parent::initialize($config);

            $this->setTable('ws_icing_user_groups');
            $this->setDisplayField('name');
            $this->setPrimaryKey('id');

            $this->addBehavior('Timestamp');

            $this->hasMany(
                'Icing.Users',
                [
                    'foreignKey' => 'user_group_id'
                ]
            );

            $this->belongsToMany(
                'Icing.Roles',
                [
                    'foreignKey'       => 'user_group_id',
                    'targetForeignKey' => 'role_id',
                    'joinTable'        => 'ws_icing_user_groups_roles'
                ]
            );
        }

        /**
         * Default validation rules.
         *
         * @param \Cake\Validation\Validator $validator Validator instance.
         *
         * @return \Cake\Validation\Validator
         */
        public function validationDefault(Validator $validator): Validator
        {
            $validator
                ->scalar('name')
                ->maxLength('name', 100)
                ->requirePresence('name', 'create')
                ->notEmptyString('name')
                ->add(
                    'name',
                    'unique',
                    [
                        'rule'     => 'validateUnique',
                        'provider' => 'table',
                        'message'  => 'That name is already in use'
                    ]
                );

            $validator
                ->scalar('homepage')
                ->maxLength('homepage', 255)
                ->allowEmptyString('homepage');

            $validator
                ->boolean('default_user_group')
                ->notEmptyString('default_user_group');

            return $validator;
        }

        /**
         * Returns a rules checker object that will be used for validating
         * application integrity.
         *
         * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
         *
         * @return \Cake\ORM\RulesChecker
         */
        public function buildRules(RulesChecker $rules): RulesChecker
        {
            $rules->add($rules->isUnique(['name']), ['errorField' => 'name']);
            $rules->add($rules->isUnique(['default_user_group'], ['allowMultipleNulls' => true]), ['errorField' => 'default_user_group']);

            $rules->add(
                function (EntityInterface|UserGroup $entity) {
                    return !($entity->isDirty('default_user_group') && $entity->getOriginal('default_user_group'));
                },
                'default_user_group',
                [
                    'errorField' => 'default_user_group',
                    'message'    => 'There must be a default user group'
                ]
            );

            return $rules;
        }

        public function beforeSave(EventInterface $event, EntityInterface|UserGroup $entity, ArrayObject $options): void
        {
            if ($entity->isNew() && !$entity->get('id')) {
                $entity->set('id', Inflector::underscore(Inflector::variable($entity->name)));
            }
        }

        public function afterSave(EventInterface $event, EntityInterface|UserGroup $entity, ArrayObject $options): void
        {
            if ($entity->get('register_default_user_group')) {
                $this->setDefaultUserGroup($entity->id);
            }
        }

        public function beforeDelete(EventInterface $event, UserGroup|EntityInterface $entity, ArrayObject $options): void
        {
            if ($entity->default_user_group) {
                $entity->setError(
                    'default_user_group',
                    'Unable to delete the default user group. Please select another default user group first.'
                );
                $event->stopPropagation();
            }
        }

        public function getDefaultUserGroupId(): string
        {
            return $this->find()
                ->orderBy(['default_user_group' => 'DESC'])
                ->select(['id'])
                ->all()
                ->extract('id')
                ->first();
        }

        public function getDefaultUserGroup(): array|EntityInterface
        {
            return $this->get($this->getDefaultUserGroupId());
        }

        public function setDefaultUserGroup($id): bool
        {
            $alias = $this->getAlias();
            $this->getConnection()->begin();
            $this->updateAll(['default_user_group' => null], []);
            if ($this->updateAll(['default_user_group' => true], ["$alias.id" => $id])) {
                $this->getConnection()->commit();
                return true;
            }
            return false;
        }


    }
