from urllib.parse import urlparse
from logging import getLogger

from django.conf import settings
from tornado import gen


log = getLogger(__name__)


class CorsMixin:
    """ Миксин, добавляющий заголовки CORS
    """
    cors_conf = settings.ISEARCH['abovemeta'].get('cors', {})

    ORIGIN_WHITELIST = set(cors_conf.get('origin_whitelist', []))
    ALLOW_CREDENTIALS = cors_conf.get('allow_credentials')
    ALLOW_HEADERS = cors_conf.get('allow_headers')
    ALLOW_METHODS = cors_conf.get('allow_methods')

    def origin_found_in_white_lists(self, origin):
        try:
            origin_host = urlparse(origin).hostname
        except Exception:
            log.exception('Cannot parse HTTP_ORIGIN', extra={'state': getattr(self, 'state', None)})
            return False

        if not origin_host:
            return False

        host_vars = {origin_host}
        parts = origin_host.split('.')
        host_vars.update('*.' + '.'.join(parts[i:])
                         for i in range(0, len(parts)))

        # Список оридженов для cors допускает указание значений в виде
        # шаблона: *.yandex-team.ru, *.tools.yandex.com
        return bool(host_vars & self.ORIGIN_WHITELIST)

    def get_cors_origin(self):
        origin = self.request.headers.get('ORIGIN')

        if origin and self.origin_found_in_white_lists(origin):
            return origin

    def set_cors_headers(self):
        cors_origin = self.get_cors_origin()
        if not cors_origin:
            return

        self.set_header("Access-Control-Allow-Origin", cors_origin)
        self.set_header('Access-Control-Allow-Credentials', self.ALLOW_CREDENTIALS)

        if self.cors_conf.get('allow_headers'):
            self.set_header('Access-Control-Allow-Headers', ', '.join(self.ALLOW_HEADERS))
        if self.cors_conf.get('allow_methods'):
            self.set_header('Access-Control-Allow-Methods', ', '.join(self.ALLOW_METHODS))

    @gen.coroutine
    def options(self):
        self.set_cors_headers()
        self.set_status(200)
        self.finish()
