<?php
/**
 * baserCMS :  Based Website Development Project <https://basercms.net>
 * Copyright (c) NPO baser foundation <https://baserfoundation.org/>
 *
 * @copyright     Copyright (c) NPO baser foundation
 * @link          https://basercms.net baserCMS Project
 * @since         5.0.0
 * @license       https://basercms.net/license/index.html MIT License
 */

namespace BaserCore\Model\Table;

use BaserCore\Model\Entity\UserGroup;
use Cake\Core\Configure;
use Cake\Event\EventInterface;
use Cake\ORM\Association\BelongsToMany;
use Cake\ORM\Behavior\TimestampBehavior as TimestampBehaviorAlias;
use Cake\Datasource\{EntityInterface, ResultSetInterface as ResultSetInterfaceAlias};
use Cake\Validation\Validator;
use BaserCore\Model\Table\Exception\CopyFailedException;
use BaserCore\Annotation\UnitTest;
use BaserCore\Annotation\NoTodo;
use BaserCore\Annotation\Checked;

/**
 * Class UserGroupsTable
 * @property UsersTable&BelongsToMany $Users
 * @uses UserGroupsTable
 */
class UserGroupsTable extends AppTable
{
    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     * @checked
     * @noTodo
     * @unitTest
     */
    public function initialize(array $config): void
    {
        parent::initialize($config);

        $this->setTable('user_groups');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');
        $this->addBehavior('Timestamp');
        $this->belongsToMany('Users', [
            'className' => 'BaserCore.Users',
            'foreignKey' => 'user_group_id',
            'targetForeignKey' => 'user_id',
            'through' => 'BaserCore.UsersUserGroups',
            'joinTable' => 'users_user_groups',
        ]);
        $this->hasMany('Permissions', [
            'className' => 'BaserCore.Permissions',
            'order' => 'id',
            'foreignKey' => 'user_group_id',
            'dependent' => true,
            'exclusive' => false,
        ]);
        $this->setDisplayField('title');
    }

    /**
     * Default validation rules.
     *
     * @param Validator $validator Validator instance.
     * @return Validator
     * @checked
     * @noTodo
     * @unitTest
     */
    public function validationDefault(Validator $validator): Validator
    {
        $validator
            ->integer('id')
            ->allowEmptyString('id', null, 'create');

        $validator
            ->scalar('name')
            ->maxLength('name', 50, __d('baser_core', 'ユーザーグループ名は50文字以内で入力してください。'))
            ->requirePresence('name', 'create', __d('baser_core', 'ユーザーグループ名を入力してください。'))
            ->notEmptyString('name', __d('baser_core', 'ユーザーグループ名を入力してください。'))
            ->add('name', [
                'name_halfText' => [
                    'rule' => 'halfText',
                    'provider' => 'bc',
                    'message' => __d('baser_core', 'ユーザーグループ名は半角のみで入力してください。')
                ],
                'name_unique' => [
                    'rule' => 'validateUnique',
                    'provider' => 'table',
                    'message' => __d('baser_core', '既に登録のあるユーザーグループ名です。')
                ]
            ]);

        $validator
            ->scalar('title')
            ->maxLength('title', 50, __d('baser_core', '表示名は50文字以内で入力してください。'))
            ->requirePresence('title', 'create', __d('baser_core', '表示名を入力してください。'))
            ->notEmptyString('title', __d('baser_core', '表示名を入力してください。'))
            ->add('title', [
                'duplicate' => [
                    'rule' => 'validateUnique',
                    'provider' => 'table',
                    'message' => __d('baser_core', '既に登録のある表示名です。')
                ]]);

        $validator
            ->scalar('auth_prefix')
            ->requirePresence('auth_prefix', 'create', __d('baser_core', '認証プレフィックスを選択してください。'))
            ->notEmptyString('auth_prefix', __d('baser_core', '認証プレフィックスを選択してください。'));

        $validator
            ->boolean('use_move_contents')
            ->allowEmptyString('use_move_contents');

        return $validator;
    }

    /**
     * ユーザーグループデータをコピーする
     *
     * @param int $id ユーザーグループID
     * @return EntityInterface|false
     * @throws CopyFailedException When copy failed.
     * @checked
     * @noTodo
     * @unitTest
     */
    public function copy($id)
    {
        if (is_numeric($id)) {
            $userGroup = $this->get($id);
        }

        unset($userGroup->id, $userGroup->created, $userGroup->modified);

        while(true) {
            $userGroup->name .= '_copy';
            $userGroup->title .= '_copy';
            $entity = $this->newEntity($userGroup->toArray());
            if ($errors = $entity->getErrors()) {
                if(!empty($errors['name']['name_unique'])) continue;
                $exception = new CopyFailedException(__d('baser_core', '処理に失敗しました。'));
                $exception->setErrors($errors);
                throw $exception;
            } else {
                break;
            }
        }

        if ($result = $this->save($entity)) {
            $permissions = $this->Permissions->find()->where(['user_group_id' => $id])->order(['sort'])->all();
            if ($permissions) {
                foreach($permissions as $permission) {
                    $permission->user_group_id = $result->id;
                    $this->Permissions->copy(null, $permission->toArray(), ['addSuffix' => false]);
                }
            }
            return $result;
        }
    }

    /**
     * 関連するユーザーを管理者グループに変更し保存する
     *
     * @param boolean $cascade
     * @return boolean
     */
    public function beforeDelete(EventInterface $event, EntityInterface $entity, \ArrayObject $options)
    {
        if (!empty($this->data['UserGroup']['id'])) {
            $id = $this->data['UserGroup']['id'];
            $this->User->unBindModel(['belongsTo' => ['UserGroup']]);
            $datas = $this->User->find('all', ...['conditions' => ['User.user_group_id' => $id]]);
            if ($datas) {
                foreach($datas as $data) {
                    $data['User']['user_group_id'] = Configure::read('BcApp.adminGroupId');
                    $this->User->set($data);
                    $this->User->save();
                }
            }
        }
    }

    /**
     * 認証プレフィックスを取得する
     *
     * @param int $id ユーザーグループID
     * @return    string
     * @checked
     * @noTodo
     * @unitTest
     */
    public function getAuthPrefix(int $id): ?string
    {
        $userGroup = $this->find()->where(['id' => $id])->first();
        if (isset($userGroup->auth_prefix)) {
            return $userGroup->auth_prefix;
        }
        return null;
    }

}
