import logging
import socket
import uuid
import json
from logging.handlers import RotatingFileHandler

from sandbox import common
import sandbox.common.types.task as ctt
import sandbox.common.types.misc as ctm
import sandbox.common.types.database as ctd
from sandbox.serviceapi.web import exceptions
from sandbox.serviceapi.web.request import Request as ServiceApiRequest
from logbroker.unified_agent.client.python import UnifiedAgentYdHandler
from load.projects.lunaparkapi.settings import base

from .tvm import tvm_client, get_blackbox_service_ticket
from .user import User, NoUidInfo


def logbroker_logger(uri):
    log_formatter = logging.Formatter('%(asctime)s %(levelname)-8s %(processName)-25s %(name)s %(message)s')
    log_for_ua = logging.Formatter('%(message)s')

    log_handler_ua = UnifiedAgentYdHandler('logbroker_logger', uri)
    log_handler_ua.setFormatter(log_for_ua)
    log_handler_file = RotatingFileHandler(filename='logbroker_agent.log', maxBytes=1024 * 10 * 10, backupCount=5)
    log_handler_file.setFormatter(log_formatter)

    logger = logging.getLogger()
    logger.setLevel(logging.INFO)

    logger.addHandler(log_handler_ua)
    logger.addHandler(log_handler_file)

    return logger


class Request(ServiceApiRequest):
    def __init__(self, req, logger, authenticate=True):

        try:
            from datetime import datetime, timezone
            ts_utc = int(datetime.now(tz=timezone.utc).timestamp())
        except ImportError:
            import time
            ts_utc = time.time()

        self.user = User()
        self.lb_logger = logbroker_logger('localhost:17000')
        self.logbroker_message = {
            'id': uuid.uuid4().hex,
            'user': None,
            'source': None,
            'auth_method': None,
            'env': base['ENV'],
            'host': socket.getfqdn(),
            'full_path': None,
            'remote_ip': None,
            'unixtime': ts_utc
        }
        super(Request, self).__init__(req, logger, authenticate)

        if authenticate:
            self.authenticate_request()

    @common.utils.singleton_classproperty
    def blackbox(self):
        import blackboxer
        return self.blackboxer.Blackbox(url=blackboxer.url_from_yenv('intranet', 'production'))

    def authenticate_request(self):

        if ctm.HTTPHeader.AUTHORIZATION in self.req.headers:
            auth_token = self.oauth_token
            self.user = self.user_from_token(auth_token)
            self.authdata = {"status": {"value": "VALID"}, "login": self.user}
            self.auth_method = ctt.AuthMethod.OAUTH
            self.logbroker_message['auth_method'] = 'OAuth'
            self.logbroker_message['user'] = self.user
            self.logger.debug('User is validated via oauth, auth data %s', self.authdata)

        if ctm.HTTPHeader.USER_TICKET in self.req.headers:
            self.logger.debug('User ticket: %s', self.req.headers[ctm.HTTPHeader.USER_TICKET])
            try:
                # tvm_client.parse_service_ticket(self.service_ticket)
                # tvm_client.parse_user_ticket(self.user_ticket)
                self.user = User(user_ticket=self.user_ticket)
                self.auth_method = ctt.AuthMethod.TVM
                self.authdata = {"status": {"value": "VALID"}, "login": self.user.staff_info.login}
                self.logbroker_message['user'] = self.user.staff_info.login
                self.logbroker_message['auth_method'] = 'TVM'
            except (common.rest.Client.HTTPError, KeyError, NoUidInfo) as ex:
                raise exceptions.Unauthorized("Error in user ticket validation: {}".format(ex))

        if ctm.HTTPHeader.SERVICE_TICKET in self.req.headers:
            try:
                check_result = tvm_client.parse_service_ticket(self.service_ticket)
                if not check_result:
                    raise exceptions.Unauthorized('Error in user ticket validation')
                if not self.user:
                    self.user = 'tvm_service_{}'.format(check_result.src)
                self.session = None
                self.auth_method = ctt.AuthMethod.TVM
                self.authdata = {'status': {'value': 'VALID'}, 'login': self.user}

                self.logbroker_message['auth_method'] = 'TVM'
                self.logbroker_message['source'] = check_result.src

                self.logger.debug('User is validated via tvm, auth data %s', self.authdata)
            except Exception:
                self.logger.error('Error in user ticket validaton', exc_info=True)
        self.logbroker_message['remote_ip'] = self.remote_ip
        self.logbroker_message['full_path'] = self.req.full_path
        self.lb_logger.info(json.dumps(self.logbroker_message))

        if not self.user:
            raise exceptions.Unauthorized('Request has no authorization info')

    def user_from_token(self, auth_token):
        return self.get_auth_session_from_token(auth_token)

    def get_auth_session_from_token(self, token):

        try:
            data = self.blackbox.oauth(
                self.remote_ip, token,
                headers={'X-Ya-Service-Ticket': get_blackbox_service_ticket()}
            )
        except self.blackboxer.ConnectionError as con_exc:
            self.logger.error('BlackboxConnectionError: %s', con_exc)
            self.req.rejected_in_progress = True
            raise exceptions.ServiceUnavailable('Blackbox unavailable.')
        except self.blackboxer.BlackboxError as exc:
            self.logger.error('BlackboxError: %s', exc)
            raise exceptions.Forbidden('Blackbox error: {}'.format(str(exc)))

        if data["status"]["value"] != "VALID":
            raise exceptions.Forbidden(
                "OAuth-token '{}' is not valid. Authorization status: {}, error: {}".format(
                    str(token)[:8], data["status"], data["error"]
                )
            )

        try:
            return data['login']
        except (KeyError, ValueError) as ex:
            raise exceptions.Forbidden(str(ex))

    def get_read_preference_for_request(self):
        return ctd.ReadPreference.PRIMARY
