from json import JSONDecodeError
from logging import getLogger
import requests
from typing import List, Optional, Dict, Any

from staff.lib import tvm2

logger = getLogger(__name__)

ATTRIBUTES = {
    'firstname': 27,
    'lastname': 28,
    'firstname_global': 212,
    'lastname_global': 213,
    'birthday': 30,
    'timezone': 33,
    'gender': 29,
    'language': 34,
}


class BlackboxError(Exception):
    pass


class Blackbox:
    def __init__(self, blackbox_url: str, service_name: str):
        self.url = blackbox_url
        self.service_name = service_name

    def get_user_info(
        self,
        uid: Optional[int] = None,
        login: Optional[str] = None,
        sid: Optional[int] = None,
        user_ip: str = '127.0.0.1',
        attributes: Optional[List[int]] = None,
        passport_fields: Optional[List[str]] = None,
    ):
        """
        Метод userinfo
        https://doc.yandex-team.ru/blackbox/reference/MethodUserInfo.html
        :param uid: uid пользователя, если есть, логин не передается
        :param login: логин, передается, если не указан uid
        :param sid: id сервиса
        :param user_ip: ip пользователя
        :param attributes: атрибуты полей (https://docs.yandex-team.ru/authdevguide/concepts/DB_About#db-attributes)
        :param passport_fields: параметр dbfields (deprecated)
        """
        headers = {
            tvm2.TVM_SERVICE_TICKET_HEADER: tvm2.get_tvm_ticket_by_deploy(self.service_name),
        }

        params = self._create_request_params(uid, login, sid, user_ip, attributes, passport_fields)

        response = requests.get(
            url=self.url,
            headers=headers,
            params=params,
        )
        try:
            response.raise_for_status()
            result = response.json()
        except requests.HTTPError:
            logger.exception('Error getting data from Blackbox')
            raise BlackboxError('Error getting data from Blackbox')
        except JSONDecodeError:
            logger.exception('Invalid answer from Blackbox')
            raise BlackboxError('Invalid answer from Blackbox')

        return self._parse_bb_answer(result)

    @staticmethod
    def _create_request_params(
        uid: Optional[int] = None,
        login: Optional[str] = None,
        sid: Optional[int] = None,
        user_ip: str = '127.0.0.1',
        attributes: Optional[List[int]] = None,
        passport_fields: Optional[List[str]] = None,
    ) -> Dict[str, Any]:
        params = {
            'format': 'json',
            'method': 'userinfo',
            'userip': user_ip,
        }

        if uid is None and login is None:
            raise BlackboxError('uid or login should be given')

        if uid is not None:
            params['uid'] = uid
        else:
            params['login'] = login

        if sid is not None:
            params['sid'] = sid
        if attributes is not None:
            params['attributes'] = ','.join([str(attr) for attr in attributes])
        if passport_fields is not None:
            params['dbfields'] = ','.join(passport_fields)

        return params

    @staticmethod
    def _parse_bb_answer(data: dict):
        if 'error' in data:
            raise BlackboxError(data['error'])

        if len(data.get('users', [])) == 0:
            raise BlackboxError('Not found user')

        return data['users'][0]

    def get_uid_by_login(self, login: str):
        """
        Получить UID по логину пользователя, else None.
        """

        result = self.get_user_info(login=login)

        try:
            return int(result['uid']['value'])
        except ValueError:
            return None
