import mistune
import requests
from django.conf import settings
from django.utils.translation import gettext_lazy as _
from rest_framework.exceptions import APIException

from common.utils.common import get_logger
from jumpserver.utils import get_current_request
from users.utils import construct_user_email, flatten_dict, map_attributes

logger = get_logger(__name__)

SLACK_REDIRECT_URI_SESSION_KEY = '_slack_redirect_uri'


class URL:
    AUTHORIZE = 'https://slack.com/oauth/v2/authorize'
    ACCESS_TOKEN = 'https://slack.com/api/oauth.v2.access'
    GET_USER_INFO_BY_USER_ID = 'https://slack.com/api/users.info'
    SEND_MESSAGE = 'https://slack.com/api/chat.postMessage'
    AUTH_TEST = 'https://slack.com/api/auth.test'


class SlackRenderer(mistune.HTMLRenderer):
    def heading(self, text, level):
        return '*' + text + '*\n'

    def strong(self, text):
        return '*' + text + '*'

    def list(self, text, *args, **kwargs):
        lines = text.split('\n')
        for i, line in enumerate(lines):
            if not line:
                continue
            prefix = '• '
            lines[i] = prefix + line[4:-5]
        return '\n'.join(lines)

    def block_code(self, code, lang=None):
        return f'`{code}`'

    def link(self, link, text=None, title=None):
        if title or text:
            label = str(title or text).strip()
            return f'<{link}|{label}>'
        return f'<{link}>'

    def paragraph(self, text):
        return f'{text.strip()}\n'

    def linebreak(self):
        return '\n'


class SlackRequests:
    def __init__(self, client_id=None, client_secret=None, bot_token=None):
        self._client_id = client_id
        self._client_secret = client_secret
        self._bot_token = bot_token
        self.access_token = None
        self.user_id = None

    def add_token(self, headers, with_bot_token, with_access_token):
        if with_access_token:
            headers.update({'Authorization': f'Bearer {self.access_token}'})
        if with_bot_token:
            headers.update({'Authorization': f'Bearer {self._bot_token}'})

    def request(self, method, url, with_bot_token=True, with_access_token=False, **kwargs):
        headers = kwargs.pop('headers', {})
        self.add_token(headers, with_bot_token=with_bot_token, with_access_token=with_access_token)

        func_handler = getattr(requests, method, requests.get)
        data = func_handler(url, headers=headers, **kwargs).json()
        if not data.get('ok'):
            raise APIException(
                detail=data.get('error', _('Unknown error occur'))
            )
        return data

    def request_access_token(self, code):
        request = get_current_request()
        data = {
            'code': code, 'client_id': self._client_id, 'client_secret': self._client_secret,
            'grant_type': 'authorization_code',
            'redirect_uri': request.session.get(SLACK_REDIRECT_URI_SESSION_KEY)
        }
        response = self.request(
            'post', url=URL().ACCESS_TOKEN, data=data, with_bot_token=False
        )
        self.access_token = response['access_token']
        self.user_id = response['authed_user']['id']


class Slack:

    def __init__(self, client_id=None, client_secret=None, bot_token=None, **kwargs):
        self._client = SlackRequests(
            client_id=client_id, client_secret=client_secret, bot_token=bot_token
        )
        self.markdown = mistune.Markdown(renderer=SlackRenderer())

    @property
    def attributes(self):
        return settings.SLACK_RENAME_ATTRIBUTES

    def get_user_id_by_code(self, code):
        self._client.request_access_token(code)
        response = self._client.request(
            'get', f'{URL().GET_USER_INFO_BY_USER_ID}?user={self._client.user_id}',
            with_bot_token=False, with_access_token=True
        )
        return self._client.user_id, response['user']

    def is_valid(self):
        return self._client.request('post', URL().AUTH_TEST)

    def convert_to_markdown(self, message):
        blocks = []
        for line in message.split('\n'):
            block = self.markdown(line)
            if not block:
                continue
            if block.startswith('<hr>'):
                block_item = {'type': 'divider'}
            else:
                block_item = {
                    "type": "section",
                    "text": {"type": "mrkdwn", "text": block}
                }
            blocks.append(block_item)
        return {'blocks': blocks}

    def send_text(self, user_ids, msg_body):
        body = self.convert_to_markdown(msg_body)
        logger.info(f'Slack send text: user_ids={user_ids}')
        for user_id in user_ids:
            body['channel'] = user_id
            try:
                self._client.request('post', URL().SEND_MESSAGE, json=body)
            except APIException as e:
                # 只处理可预知的错误
                logger.exception(e)

    @staticmethod
    def default_user_detail(data, user_id):
        username = data.get('id', user_id)
        username = data.get('name', username)
        name = data.get('real_name', username)
        email = data.get('profile.email')
        email = construct_user_email(username, email)
        return {
            'username': username, 'name': name, 'email': email
        }

    def get_user_detail(self, user_id, **kwargs):
        # https://api.slack.com/methods/users.info
        # get_user_id_by_code 已经返回个人信息，这里直接解析
        data = kwargs['other_info']
        data['user_id'] = user_id
        info = flatten_dict(data)
        default_detail = self.default_user_detail(data, user_id)
        detail = map_attributes(default_detail, info, self.attributes)
        return detail
