import requests
from datacloud.dev_utils.logging.logger import get_basic_logger
from datacloud.dev_utils.id_value.encoders import encode_cid
from datacloud.dev_utils.tvm import tvm_utils, tvm_id
from datacloud.dev_utils.bigb import bigb_utils

logger = get_basic_logger(__name__)


__all__ = [
    'CookieSyncApi'
]


class CookieSyncApi:
    def __init__(self, cookie_sync_set, timeout=1, is_prod=True):
        """
        Args:
            cookie_sync_set (datacloud.score_api.storage.cookie_sync.CookieSyncSet):
                Storage that can validate if cookie supports cookie-sync
        """
        self._cookie_sync_set = cookie_sync_set
        self._timeout = timeout
        self._tvm_manager = tvm_utils.TVMManager()
        self._is_prod = is_prod
        if self._is_prod:
            self._url = 'http://idserv.rtcrypta.yandex.net:8080/json/identify'
            self.crypta_identify_url = 'http://id.crypta.yandex.net/identify?yandexuid={yuid}'
            self._cookie_sync_url = 'http://cm.crypta.yandex.net/identify'
            self._tvm_src = tvm_id.DATACLOUD.prod
            self._tvm_identify = tvm_id.IDENTIFY.prod
            self._tvm_cookiesync = tvm_id.COOKIEMATCH.prod
        else:
            self._url = 'http://idserv-test.rtcrypta.yandex.net:8080/json/identify'
            self._cookie_sync_url = 'http://cm-test.crypta.yandex.net/identify'
            self.crypta_identify_url = 'http://id-test.crypta.yandex.net/identify?yandexuid={yuid}'
            self._tvm_src = tvm_id.DATACLOUD.test
            self._tvm_identify = tvm_id.IDENTIFY.test
            self._tvm_cookiesync = tvm_id.COOKIEMATCH.test

    # TODO: XPROD-1392 Switch to get_matches_v2 when it will be ready and refactor CookieSyncApi
    def get_matches_v2(self, cookie):
        payload = {
            'type': cookie['cookie_vendor'],
            'value': cookie['cookie']
        }
        yuid = None
        try:
            resp = self._tvm_manager.tvm_get_request(
                self._cookie_sync_url, self._tvm_src, self._tvm_cookiesync,
                params=payload, timeout=self._timeout
            )
            if resp.status_code != 200:
                return yuid, None
            data = resp.json()[0]
            if data and data.get('type') == 'yandexuid' and 'value' in data:
                yuid = data['value']
        except Exception as ex:
            logger.info('[CookieSync] Can not get cookie match: {}'.format(ex))
        return yuid, None  # TODO Remove None during refactoring

    def get_matches(self, cookie):
        """ Get cid and yuid using cookie-sync api
        Args:
            partner_tag (str): Tag of the partner in cookie-sync system
            cookie {'cookie_vendor': '...', 'cookie': '...'}: cookie from partner
        """
        payload = {
            'header.type': 'IDENTIFY',
            'header.version': 'VERSION_4_10',
            'body.ext_id': '{tag}.{ext_uid}'.format(tag=cookie['cookie_vendor'], ext_uid=cookie['cookie']),
            'header.client': 'xprod'
        }
        data = {}
        try:
            data = requests.get(self._url, params=payload, timeout=self._timeout).json()
        except (ValueError, requests.exceptions.Timeout, requests.exceptions.RequestException) as ex:
            logger.warn('Can not get cookie-match {}'.format(ex))

        yuid, cid = None, None
        if 'header' in data:
            status = data['header'].get('status')
            if status == 'OK':
                if 'body' in data:
                    cid = data['body'].get('crypta_id')
                    yuid = data['body'].get('yuid')
            else:
                logger.warn(' Request status is {} != OK'.format(data['header']))
        else:
            logger.warn('No header in response.')
        return yuid, cid

    def _yuid_to_cid(self, yuid):
        try:
            return bigb_utils.yuid_to_cid(self._tvm_manager, yuid, self._tvm_src,
                                          timeout=self._timeout)
        except Exception as ex:
            logger.warn('Can not get cid from yuid during cookiesync: {}'.format(ex))
            return None

    def lookup_cids(self, cookie_list):
        # TODO: Add tests
        yuids = self._lookup_yuids(cookie_list)
        cids = map(self._yuid_to_cid, yuids)
        cids = filter(None, cids)
        cids = map(str, cids)
        hashed_cids = set(map(encode_cid, cids))
        return hashed_cids

    def _lookup_yuids(self, cookie_list):
        yuids = set()
        for cookie in cookie_list:
            yuid, _ = self.get_matches(cookie)
            if yuid:
                yuids.add(yuid)
        return yuids

    def separate_casual_and_sync_cookies(self, cookie_list):
        """ Returns two lists of cookies.
            First contains casual cookies, second contains cookies that support cookie-sync """
        sync_cookies, casual_cookies = [], []
        for cookie in cookie_list:
            if self._cookie_sync_set.is_sync_cookie(cookie):
                sync_cookies.append(cookie)
            else:
                casual_cookies.append(cookie)
        return casual_cookies, sync_cookies
