import threading

import grpc
import logging

from .utils import get_creds
from .config import ENV_CONFIG, ROOT_CERT_FILE
from .grpc_options import COMMON_CHANNEL_OPTIONS
from load.projects.cloud.cloud_helper.metadata_compute import SaToken
from yc_as_client.entities import Folder
from yc_as_client.client import YCAccessServiceClient, entities


class IAM:
    _client = None
    lock = threading.Lock()
    logger = logging.getLogger(__name__)

    @classmethod
    def get_client(cls):
        with cls.lock:
            if cls._client is None:
                # Если верить этому комментарию
                # https://stackoverflow.com/questions/62570677/how-python-grpc-client-manage-connections#comment111024624_62761510
                # channel сам умеет поддерживать жизнеспособность соединения.
                # так что не должно быть проблем с тем, что у нас на всё время жизни сервиса будет один канал.
                token = SaToken.get()
                cls.logger.debug(f"AS with {ENV_CONFIG.ACCESS_SERVICE_HOST}:{ENV_CONFIG.ACCESS_SERVICE_PORT}")
                channel = grpc.secure_channel(
                    f"{ENV_CONFIG.ACCESS_SERVICE_HOST}:{ENV_CONFIG.ACCESS_SERVICE_PORT}",
                    get_creds(ENV_CONFIG.ACCESS_SERVICE_HOST, ENV_CONFIG.ACCESS_SERVICE_PORT, token,
                              root_cert=ROOT_CERT_FILE),
                    options=COMMON_CHANNEL_OPTIONS,
                )
                cls._client = YCAccessServiceClient(channel=channel)
            return cls._client

    @classmethod
    def authenticate(cls, user_token, request_id):
        user: entities.UserAccountSubject = cls.get_client().authenticate(user_token, request_id=request_id)
        cls.logger.debug('Authentication was successful')
        return user.id

    @classmethod
    def authorize(cls, folder_id, permission, user_token, request_id):
        user = cls.get_client().authorize(permission, Folder(folder_id), iam_token=user_token, request_id=request_id)
        cls.logger.debug('Authorization was successful')
        return user.id
