import cv2
import numpy as np
from flask import current_app
from skimage.morphology import skeletonize

from app.constants import DiseaseGrade


def compute_count(masks):
    """计算病害数量（通用）"""
    return masks.shape[0]


def compute_perimeter(masks):
    """计算病害周长（通用）"""
    contours, _ = cv2.findContours(masks, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    total_perimeter = sum(cv2.arcLength(cnt, closed=True) for cnt in contours)
    return float(total_perimeter)


def compute_area(masks):
    """计算病害面积（通用）"""
    return float(np.sum(masks))


def compute_shape_complexity(perimeter, area):
    """计算形状复杂度（通用）"""
    if perimeter == 0:  # 避免除零错误
        return 0.0
    return float(1 - (4 * np.pi * area) / (perimeter ** 2))


def compute_texture_roughness(masks):
    """计算纹理粗糙度，使用 Laplacian 变换后的标准差（通用）"""
    # 将掩码转换为灰度图像（0 和 1 转换到 0 和 255）
    gray_mask = (masks * 255).astype(np.uint8)
    # 计算 Laplacian 变换结果
    laplacian = cv2.Laplacian(gray_mask, cv2.CV_64F)
    # 计算 Laplacian 值的标准差作为纹理粗糙度
    texture_roughness = np.std(laplacian)
    return float(texture_roughness)


def compute_crack_width(masks):
    """计算裂缝宽度（仅针对裂缝）"""
    # 骨架化
    skeleton = skeletonize(masks.astype(bool))
    # 距离变换
    dist_transform = cv2.distanceTransform((masks * 255).astype(np.uint8), cv2.DIST_L2, 5)
    crack_widths = dist_transform[skeleton]
    return float(2 * np.median(crack_widths)) if crack_widths.size > 0 else 0.0


def compute_avg_hue(masks, image):
    """计算平均色调（仅针对锈蚀）"""
    hsv_image = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
    hue_values = hsv_image[masks == 1, 0]  # 取病害区域的 hue 通道
    if hue_values.size == 0:
        return 0.0
    H_max = 179  # OpenCV HSV 色调最大值
    adjusted_hue = H_max - np.median(hue_values)
    return float(adjusted_hue)


def min_max_normalize(value, min_value, max_value):
    """ 归一化函数，使数据在 0 到 1 之间 """
    if max_value == min_value:
        return 0  # 避免除零错误
    return (value - min_value) / (max_value - min_value)


def evaluate_disease_severity(disease_count, disease_perimeter, disease_area, shape_complexity, texture_roughness,
                              crack_width, avg_hue, media):
    """
    根据病害的多个指标，计算病害严重性分数和病害等级（轻度、中度、重度、严重）。

    :param disease_count: 病害数量
    :param disease_perimeter: 病害周长
    :param disease_area: 病害面积
    :param shape_complexity: 形状复杂度
    :param texture_roughness: 纹理粗糙度
    :param crack_width: 裂缝宽度
    :param avg_hue: 平均色调
    :param media: 媒体
    :return: 病害严重性分数和病害等级（轻度、中度、重度、严重）
    """

    # 读取指标权重配置
    weights = current_app.config['DISEASE_INDEX_WEIGHTS']

    # 读取裂缝缩放因子配置
    scale_factor = current_app.config['CRACK_SCALA_FACTOR']

    # 动态计算各指标最大值
    max_disease_count = (media.resolution_width * media.resolution_height * disease_count) // disease_area \
        if disease_area else 0
    max_disease_perimeter = float((2 * (media.resolution_width + media.resolution_height)))
    max_disease_area = float(media.resolution_width * media.resolution_height)
    max_crack_width = float(min(media.resolution_width, media.resolution_height) * scale_factor)
    min_max_values = {
        'disease_count': (0, max_disease_count),
        'disease_perimeter': (0.0, max_disease_perimeter),
        'disease_area': (0.0, max_disease_area),
        'shape_complexity': (0.0, 1.0),
        'texture_roughness': (0.0, 1020.0),
        'crack_width': (0.0, max_crack_width),
        'avg_hue': (0.0, 179.0)
    }
    current_app.logger.debug(f'【评估病害】部分动态指标最大值：{min_max_values}')

    # 构造一个参数字典，确保所有需要的键都在其中
    params = {
        'disease_count': disease_count,
        'disease_perimeter': disease_perimeter,
        'disease_area': disease_area,
        'shape_complexity': shape_complexity,
        'texture_roughness': texture_roughness,
        'crack_width': crack_width,
        'avg_hue': avg_hue,
    }

    # 归一化
    normalized_values = {
        key: min_max_normalize(params[key], *min_max_values[key])
        for key in weights.keys()
    }
    current_app.logger.debug(f'【评估病害】归一化病害指标：{normalized_values}')

    # 计算加权总分
    weighted_score = sum(normalized_values[key] * weights[key] for key in weights)

    # 根据得分确定病害等级和描述
    if weighted_score >= 0.8:
        return weighted_score, DiseaseGrade.CRITICAL.value, '病害情况严重，需要立即采取修复措施。'
    elif weighted_score >= 0.5:
        return weighted_score, DiseaseGrade.SEVERE.value, '病害情况重度，应尽快安排修复。'
    elif weighted_score >= 0.2:
        return weighted_score, DiseaseGrade.MODERATE.value, '病害情况中等，应安排维护。'
    else:
        return weighted_score, DiseaseGrade.MILD.value, '病害情况轻微，定期观察即可。'
