from datetime import datetime
import time
import urllib.parse

from ylog.context import log_context

from blackboxer import AsyncBlackbox
from blackboxer.exceptions import HTTPError

from intranet.domenator.src.logic.tvm2 import get_service_ticket
from intranet.domenator.src.settings import config
from .exceptions.common import FailedDependencyException
from enum import Enum
from intranet.domenator.src.domenator_logging.logger import get_logger

requests_log = get_logger(log_name='dom_requests')
error_log = get_logger(log_name='dom_error')


class Attributes(Enum):
    FIRSTNAME = '27'
    LASTNAME = '28'
    BIRTHDAY = '30'
    COUNTRY = '31'
    CITY = '32'
    ENABLED = '1009'


class Aliases(Enum):
    PDD_ALIAS = '7'


class BlackboxClient:
    CLIENT_NAME = 'blackbox'

    def __init__(self):
        self.client = AsyncBlackbox(url=config.blackbox_url, timeout=2)
        self.blackbox_tvm2_client = config.blackbox_tvm2_client

    async def _request(self, http_method='GET', blackbox_method=None, required_fields={}, **kwargs):
        service_ticket = await get_service_ticket(self.blackbox_tvm2_client)
        if blackbox_method is None:
            raise ValueError('blacbox_method should be set')

        start_time = time.time()

        result = await self.client.custom_method(
            http_method=http_method,
            blackbox_method=blackbox_method,
            headers={'X-Ya-Service-Ticket': service_ticket},
            required_fields=required_fields,
            **kwargs,
        )

        request_time = time.time() - start_time
        url = urllib.parse.urljoin(self.client.url, blackbox_method)

        context_data = {
            'client': self.CLIENT_NAME,
            'url': url,
            'method': http_method.upper(),
            'request_time': request_time,
        }

        with log_context(upstream=context_data):
            requests_log.info(
                '%s %s: %s took %.3fs',
                http_method.upper(),
                url,
                '200',
                request_time,
                extra={
                    'request_time': request_time,
                    'client_name': self.CLIENT_NAME,
                }
            )
        return result

    async def get_domain_info(self, domain_name: str = None, admin_uid: str = None, with_aliases: bool = False) -> dict:
        if not domain_name and not admin_uid:
            raise ValueError('Specify domain_name or admin_uid')

        params = {}
        if domain_name:
            params['domain'] = domain_name
        if admin_uid:
            params['domain_admin'] = admin_uid

        try:
            domain_info = await self._request(
                blackbox_method='hosted_domains',
                aliases=int(with_aliases),
                **params,
            )

        except HTTPError as exc:
            if exc.response.code == 500:
                raise FailedDependencyException('blackbox')
            raise
        if 'hosted_domains' in domain_info and domain_info['hosted_domains']:
            info = domain_info['hosted_domains'][0]

            result = {
                'admin_id': int(info['admin']),
                'registration_date': datetime.strptime(
                    info['born_date'],
                    '%Y-%m-%d %H:%M:%S'
                ),
                'domain_id': info['domid'],
                'master_domain': info['master_domain'] or None,
                'mx': info['mx'],
                'blocked': info.get('ena', '1') == '0',
                'used_in_pdd': True  # https://st.yandex-team.ru/PASSPORTDUTY-659#61f8178aea9ef145afb9f111
            }
            if with_aliases:
                result['aliases'] = [_f for _f in info.get(
                    'slaves', '').split(',') if _f]
            return result

    async def get_userinfo(self, uid: str, ip: str):
        required_fields = {
            'userip': ip,
            'uid': uid
        }

        dbfields = [
            'userinfo.lang.uid',
            'userinfo.country.uid'
        ]

        try:
            raw_userinfo = await self._request(
                blackbox_method='userinfo',
                required_fields=required_fields,
                dbfields=','.join(dbfields),
                attributes=','.join([attr.value for attr in Attributes]),
                aliases=','.join([alias.value for alias in Aliases]),
            )

            if 'users' not in raw_userinfo or len(raw_userinfo['users']) == 0:
                return None

            userinfo = raw_userinfo['users'][0]
            attrs = userinfo.get('attributes', {})
            aliases = userinfo.get('aliases', {})

            resp = {
                'first_name': attrs.get(Attributes.FIRSTNAME.value, ''),
                'last_name': attrs.get(Attributes.LASTNAME.value, ''),
                'login': userinfo['login'],
                'pdd_alias': aliases.get(Aliases.PDD_ALIAS.value, ''),
                'enabled': attrs.get(Attributes.ENABLED, '0') == '1',
                'uid': uid,
            }

            birthday = attrs.get(Attributes.BIRTHDAY.value, None)

            if birthday is not None:
                resp['birthdate'] = birthday

            return resp

        except HTTPError as exc:
            if exc.response.code == 500:
                raise FailedDependencyException('blackbox')
            raise


async def get_blackbox_client() -> BlackboxClient:
    return BlackboxClient()


async def get_used_domains(domains: [str]) -> {str}:
    used_domains = set()
    blackbox_client = await get_blackbox_client()
    for domain in domains:
        try:
            bb_domain = await blackbox_client.get_domain_info(domain)
            if bb_domain is not None and bb_domain['used_in_pdd']:
                used_domains.add(domain)

        except Exception:
            error_log.error('Error on request blackbox', exc_info=True)

    return used_domains
