import hashlib
import json
import os
import random
import time
from io import BytesIO
from typing import List, Tuple

import cv2
import numpy as np
from nonebot import logger

import aiohttp
import requests
import requests.exceptions
from PIL import Image, ImageDraw, ImageFont

filepath = os.path.dirname(__file__).replace("\\", "/")


# 圆角遮罩处理
def draw_rect(img, pos, radius, **kwargs):
    trans = Image.new('RGBA', img.size, (0, 0, 0, 0))
    alpha_draw = ImageDraw.Draw(trans, "RGBA")
    alpha_draw.rounded_rectangle(pos, radius, **kwargs)
    img.paste(Image.alpha_composite(img, trans))
    return img


# 圆角处理
def circle_corner(img, radii):
    """
    半透明圆角处理
    :param img: 要修改的文件
    :param radii: 圆角弧度
    :return: 返回修改过的文件
    """
    circle = Image.new('L', (radii * 2, radii * 2), 0)  # 创建黑色方形
    draw = ImageDraw.Draw(circle)
    draw.ellipse((0, 0, radii * 2, radii * 2), fill=255)  # 黑色方形内切白色圆形

    img = img.convert("RGBA")
    w, h = img.size

    # 创建一个alpha层，存放四个圆角，使用透明度切除圆角外的图片
    alpha = Image.new('L', img.size, 255)
    alpha.paste(circle.crop((0, 0, radii, radii)), (0, 0))  # 左上角
    alpha.paste(circle.crop((radii, 0, radii * 2, radii)),
                (w - radii, 0))  # 右上角
    alpha.paste(circle.crop((radii, radii, radii * 2, radii * 2)),
                (w - radii, h - radii))  # 右下角
    alpha.paste(circle.crop((0, radii, radii, radii * 2)),
                (0, h - radii))  # 左下角
    img.putalpha(alpha)  # 白色区域透明可见，黑色区域不可见

    # 添加圆角边框
    draw = ImageDraw.Draw(img)
    draw.rounded_rectangle(img.getbbox(), outline="white", width=3, radius=radii)
    return img


# PNG重绘大小
def png_resize(source_file, new_width=0, new_height=0, resample="LANCZOS", ref_file=''):
    """
    PNG缩放透明度处理
    :param source_file: 源文件（Image.open()）
    :param new_width: 设置的宽度
    :param new_height: 设置的高度
    :param resample: 抗锯齿
    :param ref_file: 参考文件
    :return:
    """
    img = source_file
    img = img.convert("RGBA")
    width, height = img.size

    if ref_file != '':
        imgRef = Image.open(ref_file)
        new_width, new_height = imgRef.size
    else:
        if new_height == 0:
            new_height = new_width * width / height

    bands = img.split()
    resample_map = {
        "NEAREST": Image.NEAREST,
        "BILINEAR": Image.BILINEAR,
        "BICUBIC": Image.BICUBIC,
        "LANCZOS": Image.LANCZOS
    }
    resample_method = resample_map.get(resample, Image.LANCZOS)  # 默认使用 LANCZOS

    bands = [b.resize((new_width, new_height), resample=resample_method) for b in bands]
    resized_file = Image.merge('RGBA', bands)

    return resized_file


# 图片粘贴
def image_paste(paste_image, under_image, pos):
    """

    :param paste_image: 需要粘贴的图片
    :param under_image: 底图
    :param pos: 位置（x,y）坐标
    :return: 返回图片
    """
    # 获取需要贴入图片的透明通道
    r, g, b, alpha = paste_image.split()
    # 粘贴时将alpha值传递至mask属性
    under_image.paste(paste_image, pos, alpha)
    return under_image


def image_paste_center(paste_image: Image, under_image: Image, pos):
    """
    将一张图片粘贴到另一张图片上，并使粘贴图片的Y轴中心对齐到指定y坐标

    :param paste_image: 需要粘贴的图片 (RGBA)
    :param under_image: 底图 (RGBA)
    :param pos: 粘贴位置 (x, y)，其中y表示目标对齐的中心位置
    :return: 返回合成后的图片
    """
    x, y = pos
    # 计算上下居中偏移
    paste_w, paste_h = paste_image.size
    y_top = y - paste_h // 2  # 上边位置 = 中心点y - 半高

    # 获取透明通道
    r, g, b, alpha = paste_image.split()
    under_image.paste(paste_image, (x, y_top), alpha)
    return under_image


def download_icon(url):
    data = download_url(url)
    img = BytesIO(data)
    return img


# 下载QQ头像
def download_avatar(user_id: str) -> bytes:
    url = f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=640"
    data = download_url(url)
    if not data or hashlib.md5(data).hexdigest() == "acef72340ac0e914090bd35799f5594e":
        url = f"http://q1.qlogo.cn/g?b=qq&nk={user_id}&s=100"
        data = download_url(url)
    return data


# 根据URL下载文件
def download_url(url: str) -> bytes:
    for i in range(3):
        try:
            resp = requests.get(url)
            if resp.status_code != 200:
                continue
            return resp.content
        except Exception as e:
            print(f"Error downloading {url}, retry {i}/3: {str(e)}")


# 图片裁剪
def cut_image(pic_data: bytes, target_ratio: float):
    try:
        pic_data = Image.open(BytesIO(pic_data))
        w, h = pic_data.size
        pic_ratio = w / h
        if pic_ratio > target_ratio:
            # 宽高比大于目标比例，按高度缩放。保持宽度不变
            new_h = w / target_ratio
            h_delta = (h - new_h) / 2
            w_delta = 0
        else:
            # 宽高比小于目标比例，按宽度缩放。保持高度不变
            new_w = h * target_ratio
            w_delta = (w - new_w) / 2
            h_delta = 0
        cropped = pic_data.crop((w_delta, h_delta, w - w_delta, h - h_delta))
        return cropped
    except Exception as e:
        raise Exception(f"图片剪裁失败{str(e)}")


def draw_centered_text(draw, text, x_left, x_right, y, font, fill):
    """
    在指定区域水平居中绘制文本
    :param draw: ImageDraw对象
    :param text: 要绘制的文本
    :param x_left: 区域左边界x坐标
    :param x_right: 区域右边界x坐标
    :param y: 文本基线y坐标
    :param font: ImageFont对象
    :param fill: 文本颜色
    """
    text = str(text)
    text_width = font.getlength(text)  # 获取文本像素宽度
    area_width = x_right - x_left
    x_center = x_left + (area_width - text_width) / 2
    draw.text((x_center, y), text, font=font, fill=fill)


def get_save_icon(game, name, icon_type, url):
    name = name.replace("/", "")
    icon = get_icon_from_cache(name, game, icon_type)
    if not icon:
        if game == "bf1":
            icon = get_icon_from_url(url)
            if icon_type == "weapon":
                icon = png_resize(icon, 500, 125)
            else:
                icon = png_resize(icon, 315, 79)
        elif game == "bf3":
            icon = get_icon_from_url(url)
            if icon_type == "weapon":
                icon = png_resize(icon, 500, 300)
            else:
                icon = png_resize(icon, 315, 200)
        elif game == "bf4":
            icon = get_icon_from_url(url)
            if icon_type == "weapon":
                icon = png_resize(icon, 500, 165)
            else:
                icon = png_resize(icon, 315, 80)
        elif game == "bfv":
            icon = get_icon_from_url(url)
            if icon_type == "weapon":
                icon = png_resize(icon, 500, 166)
            else:
                icon = png_resize(icon, 315, 315)
        icon.save(f"{filepath}/img/{game}/{icon_type}/{name}.png")
        logger.info(f"文件已经缓存至：{filepath}/img/{game}/{icon_type}/{name}.png")

    # 上述步骤处理完再执行一次icon判空
    if icon:
        return icon
    else:
        # 针对空图片处理
        if icon_type == "weapon":
            icon = Image.open(filepath + "/img/wp.png").convert('RGBA')
        else:
            icon = Image.open(filepath + "/img/vc.png").convert('RGBA')
        return icon


# 获取icon
def get_icon_from_url(url):
    if url:
        try:
            # 添加10s超时判断，如果超时返回空
            res = BytesIO(requests.get(url, timeout=10).content)
            icon = Image.open(res).convert('RGBA')
            return icon
        except requests.exceptions.RequestException as e:
            logger.error(f"请求异常：{e}")


def get_icon_from_cache(icon_name, game, icon_type):
    path = f"{filepath}/img/{game}/{icon_type}"
    try:
        icon_list = os.listdir(path)
        if icon_name in str(icon_list):
            logger.info(f"本地存在{icon_name}物品")
            img = Image.open(f"{path}/{icon_name}.png").convert('RGBA')
            return img
    except Exception as err:
        logger.error(f"获取图标失败：{str(err)}")
        return None


def cutout_region(img, mask, alpha=0):
    """
    在图像指定区域设置透明度
    :param img: 输入图像 (BGR 或 BGRA)
    :param mask: 区域定义
                 - 矩形: (x1, y1, x2, y2)
                 - 圆形: (cx, cy, r)
                 - 任意 mask: numpy.ndarray (0/255)
    :param alpha: 设置的透明度 (0=透明, 255=不透明, 0-255之间半透明)
    :return: 处理后的图像 (BGRA)
    """

    # 转为 BGRA (确保有 alpha 通道)
    if img.shape[2] == 3:
        b, g, r = cv2.split(img)
        a = np.ones(b.shape, dtype=np.uint8) * 255
        img = cv2.merge((b, g, r, a))

    h, w = img.shape[:2]
    region_mask = np.zeros((h, w), dtype=np.uint8)

    # 根据传入参数类型生成区域 mask
    if isinstance(mask, tuple):
        if len(mask) == 4:  # 矩形
            x1, y1, x2, y2 = mask
            region_mask[y1:y2, x1:x2] = 255
        elif len(mask) == 3:  # 圆形
            cx, cy, r = mask
            cv2.circle(region_mask, (cx, cy), r, 255, -1)
        else:
            raise ValueError("不支持的 mask tuple 格式")
    elif isinstance(mask, np.ndarray):  # 自定义 mask
        if mask.shape != (h, w):
            raise ValueError("mask 尺寸必须与图像一致")
        region_mask = mask
    else:
        raise TypeError("mask 必须是 tuple 或 numpy.ndarray")

    # 修改 alpha 通道
    img[region_mask > 0, 3] = alpha

    return img


def paste_image_cv2(fg, bg, pos):
    """
    将前景图粘贴到背景图上，支持透明度 (Alpha 混合)
    :param fg: 前景图 (必须是 BGRA)
    :param bg: 背景图 (BGR 或 BGRA)
    :param pos: 粘贴位置 (x, y) 左上角坐标
    :return: 合成后的图像 (BGRA)
    """

    x, y = pos
    fh, fw = fg.shape[:2]

    # 确保背景有 alpha 通道
    if bg.shape[2] == 3:
        b, g, r = cv2.split(bg)
        a = np.ones(b.shape, dtype=np.uint8) * 255
        bg = cv2.merge((b, g, r, a))

    # 截取 ROI 区域
    roi = bg[y:y + fh, x:x + fw]

    # 防止越界
    if roi.shape[0] != fh or roi.shape[1] != fw:
        raise ValueError("前景图超出背景范围")

    # 提取 alpha 通道
    alpha_fg = fg[:, :, 3] / 255.0
    alpha_bg = 1.0 - alpha_fg

    # 混合 RGB
    for c in range(3):
        roi[:, :, c] = (alpha_fg * fg[:, :, c] +
                        alpha_bg * roi[:, :, c])

    # 更新 alpha 通道（取最大值，保证透明度正确）
    roi[:, :, 3] = np.clip(fg[:, :, 3] + roi[:, :, 3], 0, 255)

    # 放回背景
    bg[y:y + fh, x:x + fw] = roi
    return bg


def notice_paste(cv2_bg):
    pos = (1176, 318)
    # 获取公告
    notice_path = f"{filepath}/notice/notice.png"
    front_img = cv2.imread(notice_path)
    mask = np.zeros((631, 1355), dtype=np.uint8)
    pts = np.array([[0, 0], [167, 0], [0, 631]], np.int32)
    cv2.fillPoly(mask, [pts], 255)
    front_img = cutout_region(front_img, mask, alpha=0)
    mix_bg = paste_image_cv2(front_img, cv2_bg, pos)
    return mix_bg
