import hashlib
import json
import logging
import os

from fontTools.ttLib import TTFont

import pkg.qzone.model
import pkg.chat.manager
import re
import threading
import time

import pkg.database.mediamgr

import requests

from PIL import Image, ImageFont, ImageDraw, ImageEnhance

inst = None


def get_qq_nickname(uin):
    url = "https://r.qzone.qq.com/fcg-bin/cgi_get_portrait.fcg?uins={}".format(uin)
    response = requests.get(url)
    text = response.content.decode('gbk', 'ignore')
    json_data = json.loads(text.replace("portraitCallBack(", "")[:-1])
    nickname = json_data[str(uin)][6]
    return nickname


extra = [u"\U00002728", u"\U0001faa2", u"\U0001f9f5", u"\U0000263a", u"\U0000274c", u"\U00002795", u"\U0001f236",
         u"\U0001f21a"]


def ensure_emoji(unicode):
    if not os.path.isfile("emojis/{}.png".format(unicode)):
        url = 'https://www.emojiall.com/images/60/apple/{}.png'.format(unicode)
        try:
            res = requests.get(url)
            if res.status_code != 200:
                return False
            with open('emojis/{}.png'.format(unicode), 'wb') as f:
                f.write(res.content)
        except:
            return False
    return True


def indexNumber(path=''):
    """
    æŸ¥æ‰¾å­—ç¬¦ä¸²ä¸­æ•°å­—æ‰€åœ¨ä¸²ä¸­çš„ä½�ç½®
    :param path:ç›®æ ‡å­—ç¬¦ä¸²
    :return:<class 'list'>: <class 'list'>: [['1', 16], ['2', 35], ['1', 51]]
    """
    kv = []
    nums = []
    beforeDatas = re.findall('[\d]+', path)
    for num in beforeDatas:
        indexV = []
        times = path.count(num)
        if times > 1:
            if num not in nums:
                indexs = re.finditer(num, path)
                for index in indexs:
                    iV = []
                    i = index.span()[0]
                    iV.append(num)
                    iV.append(i)
                    kv.append(iV)
            nums.append(num)
        else:
            index = path.find(num)
            indexV.append(num)
            indexV.append(index)
            kv.append(indexV)
    # æ ¹æ�®æ•°å­—ä½�ç½®æ�’åº�
    indexSort = []
    resultIndex = []
    for vi in kv:
        indexSort.append(vi[1])
    indexSort.sort()
    for i in indexSort:
        for v in kv:
            if i == v[1]:
                resultIndex.append(v)
    return resultIndex


def get_size(file):
    # è�·å�–æ–‡ä»¶å¤§å°�:KB
    size = os.path.getsize(file)
    return size / 1024


def get_outfile(infile, outfile):
    if outfile:
        return outfile
    dir, suffix = os.path.splitext(infile)
    outfile = '{}-out{}'.format(dir, suffix)
    return outfile


def compress_image(infile, outfile='', mb=512, step=20, quality=90):
    """ä¸�æ”¹å�˜å›¾ç‰‡å°ºå¯¸å�‹ç¼©åˆ°æŒ‡å®šå¤§å°�
    :param infile: å�‹ç¼©æº�æ–‡ä»¶
    :param outfile: å�‹ç¼©æ–‡ä»¶ä¿�å­˜åœ°å�€
    :param mb: å�‹ç¼©ç›®æ ‡,KB
    :param step: æ¯�æ¬¡è°ƒæ•´çš„å�‹ç¼©æ¯”ç�‡
    :param quality: åˆ�å§‹å�‹ç¼©æ¯”ç�‡
    :return: å�‹ç¼©æ–‡ä»¶åœ°å�€ï¼Œå�‹ç¼©æ–‡ä»¶å¤§å°�
    """
    o_size = get_size(infile)
    if o_size <= mb:
        return infile
    outfile = get_outfile(infile, outfile)
    while o_size > mb:
        im = Image.open(infile)
        im.save(outfile, quality=quality)
        if quality - step < 0:
            break
        quality -= step
        o_size = get_size(outfile)
    return outfile, get_size(outfile)


def text_color(text):
    md5 = string_to_md5(text)

    rsample = int(md5[:2], 16)  # [0,255]
    gsample = int(md5[12:14], 16)
    bsample = int(md5[-3:-1], 16)

    rn = rsample * rsample  # [0,65025]
    gn = gsample * gsample
    bn = bsample * bsample

    rlimit = int(rn * 0.0039315686)  # [0,255]
    glimit = int(gn * 0.0039315686)
    blimit = int(bn * 0.0039315686)

    r = hex(int(rlimit * 0.7 + 30))[2:].rjust(2, "0")
    g = hex(int(glimit * 0.61 + 58))[2:].rjust(2, "0")
    b = hex(int(blimit * 0.54 + 60))[2:].rjust(2, "0")

    return "#" + r + g + b


def string_to_md5(string):
    md5_val = hashlib.md5(string.encode('utf8')).hexdigest()
    return md5_val


def find_labels(text):
    return re.findall(r'#\[.+?\]#', text)


class EmotionPublisher:
    env_id = ''
    app_id = ''
    app_secret = ''
    access_token = ''
    token_refresh_timestamp = 0

    watermarker = ''

    access_token_getting_thread = None

    text_render_font = 0
    label_render_font = 0
    anonymous_nick_font = 0
    comment_text = 0
    id_text = 0

    font = 0
    uniMap = 0

    last_download_watermarker_timestamp = 0

    def __init__(self, env_id, app_id, app_secret, watermarker=''):
        global inst
        self.env_id = env_id
        self.app_id = app_id
        self.app_secret = app_secret

        self.watermarker = watermarker

        self.text_render_font = ImageFont.truetype("simhei.ttf", 32, encoding="utf-8")
        self.label_render_font = ImageFont.truetype("simhei.ttf", 20, encoding="utf-8")
        self.anonymous_nick_font = ImageFont.truetype("simhei.ttf", 45, encoding="utf-8")
        self.comment_text = ImageFont.truetype("msyh.ttc", 14, encoding="utf-8")
        self.id_text = ImageFont.truetype("msyh.ttc", 40, encoding="utf-8")

        self.font = TTFont("simhei.ttf")
        self.uniMap = self.font['cmap'].tables[0].ttFont.getBestCmap()

        inst = self

    def render_text_image(self, post, path='cache/text.png', watermarker=None, left_bottom_text=None,
                          right_bottom_text=None):

        labels = find_labels(post['text'])
        for label in labels:
            post['text'] = post['text'].replace(label, '')

        # åˆ†è¡Œ
        lines = post['text'].split('\n')

        # è®¡ç®—å¹¶åˆ†å‰²
        final_lines = []

        text_width = 525
        for line in lines:
            # å¦‚æ�œé•¿äº†å°±åˆ†å‰²
            line_width = self.text_render_font.getlength(line)
            if line_width < text_width:
                final_lines.append(line)
                continue
            else:
                rest_text = line
                while True:
                    # åˆ†å‰²æœ€å‰�é�¢çš„ä¸€è¡Œ
                    point = int(len(rest_text) * (text_width / line_width))

                    # æ£€æŸ¥æ–­ç‚¹æ˜¯å�¦åœ¨æ•°å­—ä¸­é—´
                    numbers = indexNumber(rest_text)

                    for number in numbers:
                        if number[1] < point < number[1] + len(number[0]) and number[1] != 0:
                            point = number[1]
                            break

                    final_lines.append(rest_text[:point])
                    rest_text = rest_text[point:]
                    line_width = self.text_render_font.getlength(rest_text)
                    if line_width < text_width:
                        final_lines.append(rest_text)
                        break
                    else:
                        continue
        # å‡†å¤‡ç”»å¸ƒ
        img = Image.new('RGBA', (750, max(280, len(final_lines) * 35 + 210)), (255, 255, 255, 255))
        draw = ImageDraw.Draw(img, mode='RGBA')

        print(self.last_download_watermarker_timestamp)
        # æ‰“å¹³å�°æ°´å�°
        if int(time.time()) - self.last_download_watermarker_timestamp > 3600:
            self.download_watermarker()
        if watermarker is not None and watermarker != '':
            marker_size = (250, 250)

            marker = Image.open(watermarker, mode='r').convert('RGBA')
            marker = marker.resize(marker_size)

            for i in range(marker_size[0]):
                for j in range(marker_size[1]):
                    p = marker.getpixel((i, j))
                    p = p[:-1] + (100,)
                    marker.putpixel((i, j), p)
            # åœ†è§’è’™ç‰ˆ
            mask = Image.new('RGBA', marker_size, color=(255, 255, 255, 0))
            # ç”»ä¸€ä¸ªåœ†
            mask_draw = ImageDraw.Draw(mask)
            mask_draw.ellipse((0, 0, marker_size[0], marker_size[1]), fill=(255, 255, 255, 45))

            masked = Image.new("RGBA", marker_size, color=(255, 255, 255, 100))
            masked.paste(marker, box=(0, 0))

            img.paste(masked, box=(img.size[0] - 150, img.size[1] - 150), mask=mask)

        # å¤´åƒ�
        show_avatar_path = ''

        if post['anonymous'] == 1:
            show_avatar_path = 'bag-on-head.png'
        else:
            res = requests.get('https://q1.qlogo.cn/g?b=qq&nk=' + str(post['qq']) + '&s=640')
            # ä½¿ç”¨BytesIOæ�¥å�£
            with open('cache/avatar.png', 'wb') as f:
                f.write(res.content)
            show_avatar_path = 'cache/avatar.png'

        avatar_size = (120, 120)

        avatar_image = Image.open(show_avatar_path, mode='r').convert('RGBA')
        avatar_image = avatar_image.resize(avatar_size)

        # åœ†è§’è’™ç‰ˆ
        # æ–°å»ºä¸€ä¸ªè’™æ�¿å›¾, æ³¨æ„�å¿…é¡»æ˜¯ RGBA æ¨¡å¼�
        mask = Image.new('RGBA', avatar_size, color=(0, 0, 0, 0))
        # ç”»ä¸€ä¸ªåœ†
        mask_draw = ImageDraw.Draw(mask)
        mask_draw.ellipse((0, 0, avatar_size[0], avatar_size[1]), fill=(0, 0, 0, 255))

        img.paste(avatar_image, box=(28, 34), mask=mask)

        # ç»˜åˆ¶Nick
        nick_name = ''
        nick_color = (0, 0, 0)
        if post['anonymous'] == 1:
            nick_name = 'åŒ¿å��'
            nick_color = (120, 120, 120)
        else:
            nick_name = get_qq_nickname(post['qq'])

        draw.text((170, 55), nick_name, fill=nick_color, font=self.anonymous_nick_font)

        # ç»˜åˆ¶æ ‡ç­¾

        x = 0
        for l in labels:
            ltext = l.replace("[", "").replace("]", "")
            draw.rounded_rectangle((168 + x, 102, 168 + x + len(ltext) * 17, 124), radius=5, fill=text_color(ltext))
            draw.text((170 + x, 102), ltext, fill="#FFFFFF", font=self.label_render_font)
            x += len(ltext * 18)

        # ç»˜åˆ¶æ­£æ–‡
        line_number = 0
        offset_x = 170
        offset_y = 130
        for final_line in final_lines:
            draw.text((offset_x, offset_y + 35 * line_number), final_line, fill=(0, 0, 0), font=self.text_render_font)
            # é��å�†æ­¤è¡Œ,æ£€æŸ¥æ˜¯å�¦æœ‰emoji
            idx_in_line = 0
            for ch in final_line:
                if self.is_emoji(ch):
                    emoji_img_valid = ensure_emoji(hex(ord(ch))[2:])
                    if emoji_img_valid:  # emojiå›¾åƒ�å�¯ç”¨,ç»˜åˆ¶åˆ°æŒ‡å®šä½�ç½®
                        emoji_image = Image.open("emojis/{}.png".format(hex(ord(ch))[2:]), mode='r').convert('RGBA')
                        emoji_image = emoji_image.resize((32, 32))

                        x, y = emoji_image.size

                        final_emoji_img = Image.new('RGBA', emoji_image.size, (255, 255, 255))
                        final_emoji_img.paste(emoji_image, (0, 0, x, y), emoji_image)

                        img.paste(final_emoji_img, box=(int(offset_x + idx_in_line * 32), offset_y + 35 * line_number))

                # æ£€æŸ¥å­—ç¬¦å� ä½�å®½
                char_code = ord(ch)
                if char_code >= 127:
                    idx_in_line += 1
                else:
                    idx_in_line += 0.5

            line_number += 1

        # ç»˜åˆ¶è§’è�½
        if left_bottom_text is None:
            left_bottom_text = ('åŒ¿å��ç”¨æˆ·' if post['anonymous'] else post['qq']) + " å�‘è¡¨äº� " + (
                time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(post['timestamp'])))
        if right_bottom_text is None:
            right_bottom_text = "å¼€å�‘ @RockChinQ | @Soulter"

        draw.text((25, img.size[1] - 25), left_bottom_text, fill=(130, 130, 130), font=self.comment_text)
        draw.text((535, img.size[1] - 25), right_bottom_text, fill=(130, 130, 130), font=self.comment_text)

        # ç»˜åˆ¶æ ‡å�·
        id_str = "{}".format(post['id'])
        draw.text((img.size[0] - (len(id_str) * 20 + 90), 20), "##" + id_str, fill=(220, 220, 220), font=self.id_text)

        img.save(path)

        return path

    def is_char_in_font(self, content):
        return ord(content) in self.uniMap.keys()

    def is_emoji(self, content):
        if not content:
            return False

        icif = not self.is_char_in_font(content)

        return icif

        # if u"\U0001F300" <= content <= u"\U0001F9EF" or content in extra:
        #     # print("is emoji")
        #     return True
        # else:
        #     print("not emoji")
        #     return False

    def get_access_token_loop(self):
        while True:
            time.sleep(1800)
            try:
                self.refresh_access_token()
            except Exception as e:
                logging.error("è�·å�–å°�ç¨‹åº�å‚¨å­˜access_tokenå¤±è´¥")
                logging.exception(e)

    def refresh_access_token(self, attempts=5):
        for i in range(5):
            try:
                url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid={}&secret={}".format(
                    self.app_id, self.app_secret)
                res = requests.get(url)
                resjson = json.loads(res.text)
                self.access_token = resjson["access_token"]
                self.token_refresh_timestamp = int(time.time())
                return
            except Exception as e:
                if i == attempts - 1:
                    raise e
                continue

    def prepare_and_publish_post(self, post):

        # æ¸²æŸ“æ–‡å­—
        text_image_path = self.render_text_image(post, watermarker=self.watermarker)

        # åŒ…è£…å�‘è¡¨æ–‡å­—

        links = re.findall(r'http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+',
                           post["text"])

        lnk_text = "\n"

        for lnk in links:
            lnk_text += lnk + "\n"

        text = "## {}".format(post['id']) \
               + (("\nQQ:" + post['qq'] + "\n@{{uin:{},nick:{},who:1}}"
                   .format(post["qq"], get_qq_nickname(post['qq']))) if not post['anonymous'] else "") + lnk_text

        image_files = [text_image_path]

        # ä¸‹è½½å›¾ç‰‡æ–‡ä»¶

        for media in json.loads(post['media']):
            if media.startswith("cloud:"):
                image_files.append(self.download_cloud_image(media, 'cache/{}'.format(int(time.time()))))
            else:
                image_files.append(pkg.database.mediamgr.get_inst().get_file_path(media))

        if pkg.qzone.model.get_inst() is None:
            raise Exception('qzoneåŠŸèƒ½æœªè¢«å�¯ç”¨')
        return pkg.qzone.model.get_inst().publish_emotion(text, image_files)

    def download_cloud_image(self, cloud, path):
        """ä»�å¾®ä¿¡äº‘å‚¨å­˜ä¸‹è½½å›¾ç‰‡"""
        try:
            try:
                os.mkdir(path)
            except Exception:
                pass

            if int(time.time()) - self.token_refresh_timestamp >= 3600:
                try:
                    pkg.qzone.publisher.get_inst().refresh_access_token()
                except Exception as e:
                    logging.error("åˆ·æ–°å°�ç¨‹åº�å‚¨å­˜access_tokenå¤±è´¥")
                    logging.exception(e)
                    chat_inst = pkg.chat.manager.get_inst()

                    if chat_inst is not None:
                        chat_inst.send_message_to_admins(["[bot]åˆ·æ–°å°�ç¨‹åº�å‚¨å­˜access_tokenå¤±è´¥"])
                    raise e

            url = "https://api.weixin.qq.com/tcb/batchdownloadfile?access_token=" + self.access_token
            data = '''{
                "env":"''' + self.env_id + '''",
                "file_list":[{
                    "fileid":"''' + cloud + '''",
                    "max_age":7200
                }]
            }'''
            res = requests.post(url=url, data=data)

            # print(res.text)

            result = json.loads(res.text)
            url = result["file_list"][0]["download_url"]

            res = requests.get(url)
            filetype = url.split("dot")[-1]
            with open(path + "/out." + filetype, 'wb') as f:
                f.write(res.content)

            # å�‹ç¼©å›¾ç‰‡æ–‡ä»¶
            compress_image(path + "/out." + filetype, path + "/out." + filetype)

            return path + "/out." + filetype
        except Exception as e:
            raise e

    def download_watermarker(self):
        url = 'https://q1.qlogo.cn/g?b=qq&nk=' + str(pkg.chat.manager.get_inst().uin) + '&s=640'
        print(url)
        res = requests.get(url)
        # ä½¿ç”¨BytesIOæ�¥å�£
        with open('cache/watermarker.jpg', 'wb') as f:
            f.write(res.content)
        show_avatar_path = 'cache/watermarker.jpg'
        self.last_download_watermarker_timestamp = int(time.time())
        self.watermarker = show_avatar_path


def get_inst() -> EmotionPublisher:
    global inst
    return inst


if __name__ == '__main__':
    # text = "ğŸ˜€ğŸ˜�ğŸ˜‚ğŸ¤£ğŸ˜ƒğŸ˜„ğŸ˜…ğŸ˜†ğŸ˜‰ğŸ˜ŠğŸ˜‹ğŸ˜�ğŸ˜�ğŸ˜˜ğŸ¥°ğŸ˜—ğŸ˜™ğŸ¥²ğŸ˜šğŸ™‚ğŸ¤—ğŸ¤©ğŸ¤”ğŸ¤¨ğŸ˜�ğŸ˜‘ğŸ˜¶ğŸŒ«ğŸ˜�ğŸ˜£ğŸ˜¥ğŸ˜®ğŸ¤�ğŸ˜¯ğŸ˜ªğŸ˜«ğŸ¥±ğŸ˜´ğŸ˜ŒğŸ˜›ğŸ˜œğŸ˜�ğŸ¤¤ğŸ˜’ğŸ˜“ğŸ˜”ğŸ˜•ğŸ™ƒğŸ¤‘ğŸ˜²ğŸ™�ğŸ˜�ğŸ˜ŸğŸ˜¤ğŸ˜¢ğŸ˜­ğŸ˜¦ğŸ˜§ğŸ˜¨ğŸ˜©ğŸ¤¯ğŸ˜¬ğŸ˜®ğŸ’¨ğŸ˜°ğŸ˜±ğŸ¥µğŸ¥¶ğŸ˜³ğŸ¤ªğŸ˜µğŸ˜µğŸ’«ğŸ¥´ğŸ˜ ğŸ˜¡ğŸ¤¬ğŸ˜·ğŸ¤’ğŸ¤•ğŸ¤¢ğŸ¤®ğŸ¤§ğŸ˜‡ğŸ¥³ğŸ¥¸ğŸ¥ºğŸ¤ ğŸ¤¡ğŸ¤¥ğŸ¤«ğŸ¤­ğŸ§�ğŸ¤“"
    text = "ğŸ�¢ğŸ�¢ğŸ�¢ğŸ§”ğŸ¤´ğŸ‘³ğŸ�‹ğŸ�ƒğŸ�ˆğŸ§¨âœ¨ğŸ�‰ğŸ�‰ğŸ�‰ğŸ�‰ğŸ�‰ğŸª¢ğŸª¢ğŸ¥¼ğŸ¥¼ğŸ¥½ğŸ¥½ğŸ–¼ğŸ–¼ğŸ�¨ğŸ�¨ğŸ§µğŸ�–ğŸ¥‡ğŸ¥ˆğŸ¥‰ğŸ�…ğŸ�²ğŸ�”ğŸ�•ğŸ�—ğŸ�–ğŸ¥¡ğŸ¥ ğŸ�˜ ğŸ¥  ğŸˆ¶ğŸˆš â�•ğŸ�§418068326"
    # render_text_image({
    #     "result": "success",
    #     "id": 16041,
    #     "openid": "",
    #     "qq": "1010553892",
    #     "timestamp": 1648184113,
    #     "text": text,
    #     "media": "[]",
    #     "anonymous": 0,
    #     "status": "é€šè¿‡",
    #     "review": "æ‹’ç»�:æµ‹è¯•"
    # }, path='text.png',
    #     watermarker='cache/watermarker.jpg')
