<?php

declare(strict_types=1);

namespace apexl\Io\modules\user\services;

use apexl\Io\modules\user\collections\permissionCollection;
use apexl\Io\modules\user\entities\roleEntity;
use apexl\Io\modules\user\entities\userEntity;
use apexl\Io\modules\user\enums\permissions\Common;
use apexl\Io\modules\user\exceptions\PermissionException;
use apexl\Io\modules\user\interfaces\PermissionInterface;
use apexl\Vault\exceptions\ExecutionException;
use apexl\Vault\Vault;

final readonly class UserPermissionManagerService
{
    public final const string TABLE__PERMISSIONS_USERS = 'permissions__users';

    public function __construct(
        private userEntity $user,
        private Vault $vault,
    ) {}

    /**
     * @throws PermissionException
     */
    public function can(PermissionInterface $permission): bool
    {
        if ($this->permissions()->contains(Common::BYPASS)) {
            return true;
        }

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

        return false;
    }

    /**
     * @throws PermissionException
     */
    public function permissions(bool $includeRoles = true): permissionCollection
    {

        try {
            $userId = $this->user->id;
            $permissions = permissionCollection::createFromQueryResult(
                $this->vault->select(self::TABLE__PERMISSIONS_USERS)
                    ->fields('permission')
                    ->where('user', $userId)
                    ->execute()
                    ->fetchAll()
            );
            if ($includeRoles) {
                $this->user->roles()->each(fn(roleEntity $role) => $permissions->merge(
                    $role->permissions()
                ));
            }

            return $permissions->unique();
        } catch (ExecutionException $e) {
            throw new PermissionException($e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * @throws PermissionException
     */
    public function add(PermissionInterface $permission): void
    {
        $userId = $this->user->id;
        try {
            $this->vault->insert(self::TABLE__PERMISSIONS_USERS)
                ->fields([
                    'user' => $userId,
                    'permission' => serialize($permission),
                ])->execute();
        } catch (ExecutionException $e) {
            throw new PermissionException($e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * @throws PermissionException
     */
    public function remove(PermissionInterface $permission): void
    {
        $userId = $this->user->id;
        try {
            $this->vault->delete(self::TABLE__PERMISSIONS_USERS)
                ->where('user', $userId)
                ->where('permission', serialize($permission))
                ->execute();
        } catch (ExecutionException $e) {
            throw new PermissionException($e->getMessage(), $e->getCode(), $e);
        }
    }

    /**
     * @throws PermissionException
     */
    public function clear(): void
    {
        try {
            $this->vault->delete(self::TABLE__PERMISSIONS_USERS)
                ->where('user', $this->user->id)
                ->execute();
        } catch (ExecutionException $e) {
            throw new PermissionException($e->getMessage(), $e->getCode(), $e);
        }
    }

}
