import base64
import datetime
import hashlib
import json
import os
import random
import socket
import time
from io import BytesIO
from pathlib import Path
from typing import Optional

import qrcode
from Crypto.Cipher import PKCS1_v1_5
from Crypto.PublicKey import RSA
from PIL import Image, ImageDraw, ImageFont
from httpx import AsyncClient
from nonebot import logger, get_driver, on_command
from nonebot.adapters.onebot.v11 import MessageEvent
from nonebot.params import ArgStr
from nonebot.permission import SUPERUSER
from pydantic import BaseModel

from . import dxx, path, rule
from ..models.accuont import User
from ..models.dxx import Area, Answer, Resource

USERDATA = path.DATABASE_PATH / "users.json"


class UserModel(BaseModel):
    id: int = None
    """用户ID"""
    time: int = None
    """添加时间戳"""
    self_id: int = None
    """BOT ID"""
    user_id: int = None
    """用户ID"""
    password: str = None
    """登录密码，用于登录web端"""
    group_id: int = None
    """通知群号"""
    name: str = None
    """姓名"""
    gender: str = None
    """性别"""
    mobile: str = None
    """手机号"""
    area: str = None
    """地区"""
    leader: int = None
    """团支书ID"""
    openid: str = None
    """微信认证ID"""
    dxx_id: str = None
    """大学习用户id,nid或uid"""
    university_type: str = None
    """学校类型"""
    university_id: str = None
    """学校id"""
    university: str = None
    """学校名称"""
    college_id: str = None
    """学院id"""
    college: str = None
    """学院名称"""
    organization_id: str = None
    """团支部id"""
    organization: str = None
    """团支部名称"""
    token: str = None
    """提交需要的token"""
    cookie: str = None
    """提交要用的cookie"""
    catalogue: str = None
    """提交期数"""
    auto_submit: bool = True
    """自动提交状态"""
    commit_time: int = None
    """提交时间"""


SUPERS = get_driver().config.superusers
AREA = [
    {
        "area": "湖北",
        "host": "cp.fjg360.cn",
        "referer": None,
        "origin": None,
        "url": "https://h5.cyol.com/special/weixin/sign.json",
        "status": True,
        "catalogue": None
    },
    {
        "area": "江西",
        "host": "www.jxqingtuan.cn",
        "referer": "http://www.jxqingtuan.cn/html/h5_index.html?&accessToken=",
        "origin": "http://www.jxqingtuan.cn",
        "url": "http://www.jxqingtuan.cn/pub/vol/volClass/index?userId=4363431",
        "status": True,
        "catalogue": None
    },
    {
        "area": "浙江",
        "host": "qczj.h5yunban.com",
        "referer": None,
        "origin": None,
        "url": "https://qczj.h5yunban.com/qczj-youth-learning/cgi-bin/common-api/course/current?accessToken=",
        "status": True,
        "catalogue": None
    },
    {
        "area": "上海",
        "host": "qcsh.h5yunban.com",
        "referer": None,
        "origin": None,
        "url": "https://qcsh.h5yunban.com/youth-learning/cgi-bin/user-api/course/join?accessToken=",
        "status": True,
        "catalogue": None
    },
    {
        "area": "安徽",
        "host": "dxx.ahyouth.org.cn",
        "referer": "http://dxx.ahyouth.org.cn/",
        "origin": "http://dxx.ahyouth.org.cn",
        "url": "http://dxx.ahyouth.org.cn/api/hidtoryList",
        "status": True,
        "catalogue": None
    },
    {
        "area": "四川",
        "host": "dxx.scyol.com",
        "referer": "http://scyol.com/v_prod6.02/",
        "origin": "http://scyol.com",
        "url": "https://dxx.scyol.com/api/student/commit",
        "status": True,
        "catalogue": None
    },
    {
        "area": "山东",
        "host": "qndxx.youth54.cn",
        "referer": "http://qndxx.youth54.cn/SmartLA/dxx.w?method=pageSdtwdt",
        "origin": "http://qndxx.youth54.cn",
        "url": "http://qndxx.youth54.cn/SmartLA/dxxjfgl.w?method=getNewestVersionInfo&openid=",
        "status": True,
        "catalogue": None
    },
    {
        "area": "重庆",
        "host": "qndxx.cqyouths.com",
        "referer": None,
        "origin": "http://qndxx.cqyouths.com",
        "url": "http://qndxx.cqyouths.com/new_course.json?time=",
        "status": True,
        "catalogue": None
    },
    {
        "area": "吉林",
        "host": "jqfy.jl54.org",
        "referer": None,
        "origin": "http://jqfy.jl54.org/jltw/wechat",
        "url": "http://jqfy.jl54.org/jltw/wechat",
        "status": True,
        "catalogue": None
    },
    {
        "area": "广东",
        "host": "tuanapi.12355.net",
        "referer": None,
        "origin": "https://tuan.12355.net",
        "url": "https://youthstudy.12355.net/saomah5/api/young/chapter/new",
        "status": True,
        "catalogue": None
    },
    {
        "area": "北京",
        "host": "m.bjyouth.net",
        "referer": None,
        "origin": "https://m.bjyouth.net",
        "url": "https://m.bjyouth.net/dxx/index",
        "status": True,
        "catalogue": None
    },
    {
        "area": "天津",
        "host": "admin.ddy.tjyun.com",
        "referer": None,
        "origin": "https://admin.ddy.tjyun.com",
        "url": "http://admin.ddy.tjyun.com/zm/jump/1",
        "status": True,
        "catalogue": None
    },
    {
        "area": "陕西",
        "host": "api.sxgqt.org.cn",
        "referer": None,
        "origin": "https://h5.sxgqt.org.cn",
        "url": "https://api.sxgqt.org.cn/h5sxapiv2/study/studyLink",
        "status": True,
        "catalogue": None
    },
]
RESOURCE = [
    {
        "name": "MiSans-Light.ttf",
        "url": "",
        "type": "字体",
        "size": "7.6 M"
    }, {
        "name": "answer.png",
        "url": "",
        "type": "答案背景",
        "size": "860.5 K"
    },
    {
        "name": "backgroud1.jpg",
        "url": "",
        "type": "完成截图背景",
        "size": "51.0 K"
    }, {
        "name": "backgroud2.jpg",
        "url": "",
        "type": "完成截图背景",
        "size": "50.9 K"
    }, {
        "name": "backgroud3.jpg",
        "url": "",
        "type": "完成截图背景",
        "size": "50.9 K"
    }, {
        "name": "backgroud4.jpg",
        "url": "",
        "type": "完成截图背景",
        "size": "50.7 K"
    }, {
        "name": "backgroud5.jpg",
        "url": "",
        "type": "完成截图背景",
        "size": "50.4 K"
    }
]

export_data = on_command("export_data", aliases={"导出用户数据", "导出数据"}, priority=50, permission=SUPERUSER,
                         rule=rule.must_command)
update_users = on_command("update_user", aliases={"更新用户数据", "刷新用户数据"}, priority=50, permission=SUPERUSER,
                          rule=rule.must_command)
update_resource = on_command("update_resource", aliases={"更新资源数据", "刷新资源数据"}, priority=50,
                             permission=SUPERUSER, rule=rule.must_command)


@update_resource.got(key="msg", prompt="是否刷新资源数据库信息？（是|否）")
async def update_resource_(msg: str = ArgStr("msg")) -> None:
    if msg not in ["是", "yes", "Y", "y", "YES", "true"]:
        await update_resource.finish(message="操作取消(*^▽^*)", at_sender=True, reply_message=True)
    else:
        await update_resource.send("资源重新载入中（请等待1分钟左右）······", at_sender=True, reply_message=True)
        await Resource.all().delete()
        await resource_init()
        await update_resource.finish(message="资源数据载入成功(^_−)☆", at_sender=True, reply_message=True)


@export_data.got(key="msg", prompt="是否导出用户数据至TeenStudy目录？（是|否）")
async def export_user(event: MessageEvent, msg: str = ArgStr("msg")) -> None:
    self_id = event.self_id
    if msg not in ["是", "yes", "Y", "y", "YES", "true"]:
        await export_data.finish(message="操作取消(*^▽^*)", at_sender=True, reply_message=True)
    else:
        result = await User.all().values()
        user_list = []
        for item in result:
            data = UserModel().dict()
            item["self_id"] = self_id
            data.update(**item)
            user_list.append(data)
        with open(USERDATA, "w", encoding="utf-8") as w:
            json.dump(user_list, w, indent=4, ensure_ascii=False)
        await export_data.finish(message="用户数据成功导出至TeenStudy目录(*^▽^*)", at_sender=True,
                                 reply_message=True)


@update_users.handle()
async def update_user() -> None:
    if not Path(USERDATA).exists():
        await update_users.finish(
            message="更新失败，没有找到可导入用户数据文件，请确保TeeStudy数据目录下存有用户数据文件╮(╯﹏╰）╭",
            at_sender=True, reply_message=True)


@update_users.got(key="msg", prompt="是否覆盖更新用户数据?（是|否）")
async def update_user() -> None:
    with open(USERDATA, "r", encoding="utf-8") as f:
        data_obj = json.load(f)
    await User.all().delete()
    for item in data_obj:
        data = UserModel().dict()
        data.update(**item)
        await User.create(**data)
    await update_users.finish(message="用户数据更新完成(*^▽^*)", at_sender=True, reply_message=True)


async def plugin_init():
    for item in AREA:
        if await Area.filter(area=item['area']).count():
            continue
        else:
            await Area.create(
                time=time.time(),
                area=item['area'],
                host=item['host'],
                referer=item['referer'],
                origin=item['origin'],
                url=item['url'],
                status=item['status'],
                catalogue=item['catalogue']
            )


async def admin_init():
    try:
        superuser = int(list(get_driver().config.superusers)[0])
    except AttributeError:
        superuser = -1
        logger.error("请配置好超管账号，之后重启nonebot2")
    try:
        ip = get_driver().config.dxx_ip
    except AttributeError:
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(('114.114.114.114', 12345))
        ip = s.getsockname()[0]
    result = path.getConfig()
    if result["DXX_IP"] == ip and result["SUPERUSER"] == superuser:
        return
    else:
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36 Edg/110.0.1587.41"
        }
        try:
            logger.opt(colors=True).info(
                f'<u><y>[大学习数据库]</y></u><g>加载配置公网IP</g>')
            ip = get_driver().config.dxx_ip
            logger.opt(colors=True).info(
                f'<u><y>[大学习数据库]</y></u><g>加载配置公网IP成功，启动检测公网IP访问状态</g>ip:<m>{ip}</m>')
        except AttributeError:
            ip = ''
            logger.opt(colors=True).info(
                f'<u><y>[大学习数据库]</y></u><g>加载配置IP失败，未检测到配置ip，启动自动获取公网IP</g>')
        if not ip:
            async with AsyncClient(headers=headers) as client:
                response = await client.get("http://ip.42.pl/raw")
            if response.status_code == 200:
                ip = response.text.strip()
                logger.opt(colors=True).info(
                    f'<u><y>[大学习数据库]</y></u><g>自动获取公网IP成功</g>ip:<m>{ip}</m>')
            else:
                ip = ""
                logger.opt(colors=True).warning(
                    f'<u><y>[大学习数据库]</y></u><g>自动获取公网IP失败，将自动配置局域网IP，请手动在.env.prod文件中配置公网IP，配置格式：DXX_IP="您的公网IP"</g>')
        if not ip:
            s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
            s.connect(('114.114.114.114', 12345))
            ip = s.getsockname()[0]
            logger.opt(colors=True).success(
                f'<u><y>[大学习提交 Web UI]</y></u><g>配置局域网IP成功</g>，局域网访问地址为:<m>http://{ip}:{get_driver().config.port}/TeenStudy/login</m>')
        data = {
            "TOKEN_TIME": 30,
            "SUPERUSER": int(list(SUPERS)[0]),
            "KEY": "d82ffad91168fb324ab6ebc2bed8dacd43f5af8e34ad0d1b75d83a0aff966a06",
            "ALGORITHM": "HS256",
            "PASSWORD": await to_hash("admin"),
            "DXX_IP": ip,
            "DXX_PORT": get_driver().config.port
        }
        path.saveConfig(data=data)


async def resource_init():
    base_file_path = os.path.dirname(__file__)[:-5] + "resource/"
    logger.opt(colors=True).info("<u><y>[大学习数据库]</y></u><g>➤➤➤➤➤检查资源数据✔✔✔✔✔</g>")
    for item in RESOURCE:
        if not await Resource.filter(name=item['name']).count():
            try:
                with open(base_file_path + item['name'], 'rb') as r:
                    content = r.read()
                await Resource.create(
                    time=time.time(),
                    name=item['name'],
                    type=item['type'],
                    url=item['url'],
                    size=item['size'],
                    file=content
                )
                logger.opt(colors=True).success(
                    f"<u><y>[大学习数据库]</y></u> <m>{item['type']}-{item['name']}</m> <g>更新成功!</g>")
            except Exception as e:
                logger.success(e)
                continue
    logger.opt(colors=True).success("<u><y>[大学习数据库]</y></u><g>➤➤➤➤➤资源数据更新完成✔✔✔✔✔</g>")


async def get_end_pic(id: int):
    font_data = await Resource.filter(type="字体").values()
    answer = await Answer.filter(id=id).order_by('time').values()
    title = '"青年大学习"' + answer[-1]["catalogue"]
    backgrouds = await Resource.filter(type="完成截图背景").values()
    bg_img = Image.open(BytesIO(backgrouds[random.randint(0, 4)]['file']))
    end_img = Image.open(BytesIO(answer[-1]['cover']))
    end_img = end_img.resize((1080, 2200), Image.BILINEAR)
    font = ImageFont.truetype(BytesIO(font_data[0]['file']), 45)
    img = Image.new('RGB', (end_img.width, bg_img.height), (255, 255, 255))
    draw = ImageDraw.Draw(img)
    img.paste(bg_img)
    time = (datetime.datetime.now() + datetime.timedelta(minutes=random.randint(2, 5))).strftime('%H:%M')
    draw.text((120, 30), text=time, font=font, fill='black')
    draw.text((1080 / 2 - (len(title) / 2) * 30, 130), text=title, font=font, fill='black')
    img.paste(end_img, (0, 200))
    buf = BytesIO()
    img.save(buf, format="PNG")
    base64_str = base64.b64encode(buf.getbuffer()).decode()
    content = "base64://" + base64_str
    return content


async def get_answer_pic():
    font_data = await Resource.filter(type="字体").values()
    answer_result = await Answer.all().order_by('time').values()
    backgrouds = await Resource.filter(type="答案背景").values()
    title = answer_result[-1]["catalogue"]
    answer = answer_result[-1]["answer"]
    start_time = datetime.datetime.fromtimestamp(answer_result[-1]['time']).strftime(
        "%Y年%m月%d日 %H:%M:%S")
    end_day = (datetime.datetime.fromtimestamp(answer_result[-1]["time"]) + datetime.timedelta(
        days=7)).strftime("%Y年%m月%d日")
    end_time = f'{end_day} 22:00:00'
    answer_bg = Image.open(BytesIO(backgrouds[0]['file']))
    img = Image.new('RGB', (902, 987), (255, 255, 255))
    img.paste(answer_bg)
    draw = ImageDraw.Draw(img)
    font = ImageFont.truetype(BytesIO(font_data[0]['file']), 55)
    draw.text((20, 20), text='青年大学习主题团课', fill='gold', font=font)
    font = ImageFont.truetype(BytesIO(font_data[0]['file']), 48)
    draw.text((20, 100), text=title, fill='gold', font=font)
    font = ImageFont.truetype(BytesIO(font_data[0]['file']), 40)
    draw.text((20, 180), fill='gold', font=font, text=answer)
    font = ImageFont.truetype(BytesIO(font_data[0]['file']), 40)
    draw.text((410, 550), fill='gold', font=font, text=start_time)
    font = ImageFont.truetype(BytesIO(font_data[0]['file']), 40)
    draw.text((410, 650), fill='gold', font=font, text=end_time)
    font = ImageFont.truetype(BytesIO(font_data[0]['file']), 40)
    draw.text((550, 500), text='开始时间', fill='gold', font=font)
    font = ImageFont.truetype(BytesIO(font_data[0]['file']), 40)
    draw.text((550, 600), text='结束时间', fill='gold', font=font)
    buf = BytesIO()
    img.save(buf, format="PNG")
    base64_str = base64.b64encode(buf.getbuffer()).decode()
    content = "base64://" + base64_str
    return content


async def to_hash(text: str) -> str:
    """
    哈希散列加密
    :param text:待加密文本
    :return: 加密文本
    """
    return hashlib.sha256(text.encode("utf-8")).hexdigest()


async def encrypt(text: str):
    """
    RSA加密
    :param text: 待加密文本
    :return: 加密文本
    """
    public_key = "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQD5uIDebA2qU746e/NVPiQSBA0Q3J8/G23zfrwMz4qoip1vuKaVZykuMtsAkCJFZhEcmuaOVl8nAor7cz/KZe8ZCNInbXp2kUQNjJiOPwEhkGiVvxvU5V5vCK4mzGZhhawF5cI/pw2GJDSKbXK05YHXVtOAmg17zB1iJf+ie28TbwIDAQAB\n-----END PUBLIC KEY-----"
    rsa_key = RSA.importKey(public_key)
    cipher = PKCS1_v1_5.new(rsa_key)
    cipher_text = base64.b64encode(cipher.encrypt(text.encode()))
    return cipher_text.decode()


async def distribute_area(user_id: int, area: str, catalogue: Optional[str] = None) -> dict:
    if area == "湖北":
        return await dxx.hubei(user_id=user_id, catalogue=catalogue)
    elif area == "江西":
        return await dxx.jiangxi(user_id=user_id, catalogue=catalogue)
    elif area == "浙江":
        return await dxx.zhejiang(user_id=user_id)
    elif area == "上海":
        return await dxx.shanghai(user_id=user_id)
    elif area == "安徽":
        return await dxx.anhui(user_id=user_id)
    elif area == "四川":
        return await dxx.sichuan(user_id=user_id)
    elif area == "山东":
        return await dxx.shandong(user_id=user_id, catalogue=catalogue)
    elif area == "重庆":
        return await dxx.chongqing(user_id=user_id)
    elif area == "吉林":
        return await dxx.jilin(user_id=user_id)
    elif area == "广东":
        return await dxx.guangdong(user_id=user_id)
    elif area == "北京":
        return await dxx.beijing(user_id=user_id)
    elif area == "天津":
        return await dxx.tianjin(user_id=user_id)
    elif area == "陕西":
        return await dxx.shanxi(user_id=user_id, catalogue=catalogue)
    else:
        return {
            "status": 404,
            "msg": "该地区暂未支持！"
        }


async def distribute_area_url(province: str, user_id: int, group_id: int) -> dict:
    config = path.getConfig()
    if province == "湖北":
        province = "hubei"
    elif province == "江西":
        province = "jiangxi"
    elif province == "安徽":
        province = "anhui"
    elif province == "四川":
        province = "sichuan"
    elif province == "山东":
        province = "shandong"
    elif province == "重庆":
        province = "chongqing"
    elif province == "吉林":
        province = "jilin"
    elif province == "广东":
        province = "guangdong"
    elif province == "北京":
        province = "beijing"
    elif province == "天津":
        province = "tianjin"
    elif province == "陕西":
        province = "shanxi"
    data = f"http://{config['DXX_IP']}:{config['DXX_PORT']}/TeenStudy/api/{province}?user_id={user_id}&group_id={group_id}"
    img = qrcode.make(data=data)
    buf = BytesIO()
    img.save(buf, format="PNG")
    base64_str = base64.b64encode(buf.getbuffer()).decode()
    content = "base64://" + base64_str
    return {
        "url": data,
        "content": content
    }


async def get_login_qrcode() -> dict:
    config = path.getConfig()
    data = f'http://{config["DXX_IP"]}:{config["DXX_PORT"]}/TeenStudy/login'
    img = qrcode.make(data=data)
    buf = BytesIO()
    img.save(buf, format="PNG")
    base64_str = base64.b64encode(buf.getbuffer()).decode()
    content = "base64://" + base64_str
    return {
        "url": data,
        "content": content
    }


async def get_qrcode(user_id: int, group_id: int, area: str) -> dict:
    config = path.getConfig()
    if area == "浙江":
        data = f"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx56b888a1409a2920&redirect_uri=https://wx.yunban.cn/wx/oauthInfoCallback?r_uri=http://{config['DXX_IP']}:{config['DXX_PORT']}/TeenStudy/api/zhejiang/{user_id}/{group_id}&source=common&response_type=code&scope=snsapi_userinfo&state=STATE&component_appid=wx0f0063354bfd3d19&connect_redirect=1"
    else:
        data = f"https://open.weixin.qq.com/connect/oauth2/authorize?appid=wxa693f4127cc93fad&redirect_uri=https://wx.yunban.cn/wx/oauthInfoCallback?r_uri=http://{config['DXX_IP']}:{config['DXX_PORT']}/TeenStudy/api/shanghai/{user_id}/{group_id}&source=common&response_type=code&scope=snsapi_userinfo&state=STATE&component_appid=wx0f0063354bfd3d19&connect_redirect=1"
    img = qrcode.make(data=data)
    buf = BytesIO()
    img.save(buf, format="PNG")
    base64_str = base64.b64encode(buf.getbuffer()).decode()
    content = "base64://" + base64_str
    return {
        "url": data,
        "content": content
    }
