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

import cv2
import numpy as np
from nonebot import logger

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

from src.plugins.bf_bot.user_data.data_utils import UserManager, DesignationManager

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


def png_resize(source_file, new_width=0, new_height=0, resample="LANCZOS", ref_file=""):
    """
    PNG 等比缩放（保持 Alpha 透明度）
    :param source_file: Image.open() 得到的 Image 对象
    :param new_width: 目标宽度（可选）
    :param new_height: 目标高度（可选）
    :param resample: NEAREST / BILINEAR / BICUBIC / LANCZOS
    :param ref_file: 参考图片路径（等比适配到参考尺寸）
    :return: PIL.Image (RGBA)
    """

    img = source_file.convert("RGBA")
    src_w, src_h = img.size

    # -------------------------
    # 计算目标尺寸（等比）
    # -------------------------
    if ref_file:
        ref_img = Image.open(ref_file)
        ref_w, ref_h = ref_img.size

        scale = min(ref_w / src_w, ref_h / src_h)
        target_w = int(src_w * scale)
        target_h = int(src_h * scale)

    else:
        if new_width > 0 and new_height > 0:
            scale = min(new_width / src_w, new_height / src_h)
            target_w = int(src_w * scale)
            target_h = int(src_h * scale)

        elif new_width > 0:
            scale = new_width / src_w
            target_w = new_width
            target_h = int(src_h * scale)

        elif new_height > 0:
            scale = new_height / src_h
            target_h = new_height
            target_w = int(src_w * scale)

        else:
            # 未指定任何尺寸，直接返回
            return img

    # -------------------------
    # 重采样方式
    # -------------------------
    resample_map = {
        "NEAREST": Image.NEAREST,
        "BILINEAR": Image.BILINEAR,
        "BICUBIC": Image.BICUBIC,
        "LANCZOS": Image.LANCZOS
    }
    resample_method = resample_map.get(resample, Image.LANCZOS)

    # -------------------------
    # Alpha 安全 resize（逐通道）
    # -------------------------
    bands = img.split()
    resized_bands = [
        band.resize((target_w, target_h), resample=resample_method)
        for band in bands
    ]

    return Image.merge("RGBA", resized_bands)


# 图片粘贴
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 draw_right_aligned_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)

    # 右对齐：文本右边贴到 x_right
    x_start = x_right - text_width

    # 可选：防止越界（超出左边界时截断）
    if x_start < x_left:
        x_start = x_left

    draw.text((x_start, y), text, font=font, fill=fill)


def get_save_icon(game, name, icon_type, url):
    if game == 'bf6':
        name = name.replace("/", "").upper()
    else:
        name = name.replace("/", "")
    logger.info(f"查询的物品: {name},图标类型：{icon_type},游戏：{game}")
    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}"
    logger.info(f"查询物品：{icon_name}物品路径：{path}")
    try:
        icon_list = os.listdir(path)
        # logger.info(f"所有物品：{icon_list}")
        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


def notice_paste_bf6(cv2_bg):
    x, y = 0, 0

    notice_path = f"{filepath}/notice/notice_bf6.png"
    front_img = cv2.imread(notice_path, cv2.IMREAD_UNCHANGED)

    if front_img is None:
        raise RuntimeError(f"Failed to load image: {notice_path}")

    h, w = front_img.shape[:2]

    # ROI 边界裁剪
    bg_h, bg_w = cv2_bg.shape[:2]
    w = min(w, bg_w - x)
    h = min(h, bg_h - y)

    roi = cv2_bg[y:y + h, x:x + w]

    # 有 Alpha 通道 → Alpha 混合
    if front_img.shape[2] == 4:
        alpha = front_img[:h, :w, 3] / 255.0
        alpha = alpha[:, :, None]

        cv2_bg[y:y + h, x:x + w] = (
                alpha * front_img[:h, :w, :3] +
                (1 - alpha) * roi
        ).astype(np.uint8)

    # 无 Alpha → 直接覆盖
    else:
        cv2_bg[y:y + h, x:x + w] = front_img[:h, :w]

    return cv2_bg


def designation_paste(player,
                      cv2_bg,
                      user_id,
                      start_x=10,
                      start_y=10,
                      target_height=40,
                      spacing=6
                      ):
    designation_list_path = get_designation_list_by_user_id(user_id, player)
    if not designation_list_path:
        return cv2_bg

    icons = []
    total_width = 0

    for path in designation_list_path:
        icon = cv2.imread(path, cv2.IMREAD_UNCHANGED)
        if icon is None:
            continue

        h, w = icon.shape[:2]
        scale = target_height / h
        new_w = int(w * scale)

        icon = cv2.resize(icon, (new_w, target_height))
        icons.append(icon)
        total_width += new_w

    total_width += spacing * (len(icons) - 1)

    x = start_x
    y = start_y  # 关键变化点：直接使用 top-left

    bg_h, bg_w = cv2_bg.shape[:2]

    for icon in icons:
        ih, iw = icon.shape[:2]

        if x + iw > bg_w or y + ih > bg_h:
            break

        roi = cv2_bg[y:y + ih, x:x + iw]

        if icon.shape[2] == 4:
            alpha = icon[:, :, 3] / 255.0
            for c in range(3):
                roi[:, :, c] = roi[:, :, c] * (1 - alpha) + icon[:, :, c] * alpha
        else:
            roi[:] = icon

        cv2_bg[y:y + ih, x:x + iw] = roi
        x += iw + spacing

    return cv2_bg


def get_designation_list_by_user_id(user_id, player):
    usermanager = UserManager()
    designationmanager = DesignationManager()
    user_info = usermanager.get_user_by_qq(user_id)
    icon_no_list = []
    if user_info is not None and user_info['ea_player_name'].upper() == player.upper():
        logger.info(f"玩家标签列表：{user_info}")
        icon_no_list = user_info['designation']
    icon_list = designationmanager.get_designation_by_id_list(icon_no_list)
    icon_path_list = []
    for icon in icon_list:
        icon_file_name = icon['icon_path']
        icon_path_list.append(f"{filepath}/img/icon/designation/{icon_file_name}")
    return icon_path_list


def paste_rank_icon(rank, img):
    rank_pic_path = get_rank_pic(rank)
    rank_icon_path = f"{filepath}/img/icon/rank/{rank_pic_path}"
    rank_icon = Image.open(rank_icon_path)
    rank_icon = png_resize(rank_icon, new_width=132, new_height=192)
    fm_img = image_paste(rank_icon, img, (946, 61))
    return fm_img


def get_rank_pic(rank: int) -> str:
    """
    根据 rank 返回对应的 rank_pic 路径
    """
    for (start, end), info in rank_pic.items():
        if start <= rank <= end:
            return info["path"]
    return "t_ui_rankswatch_1-9 1.png"
