"""
独立打标模块：基于预先生成的机型阈值 Excel 对风机数据打标签。

功能：
    apply_labels(df, model_name, threshold_file=None) -> DataFrame
        输入原始数据 DataFrame 和机型名称，返回打标后的 DataFrame，只包含原始列和 label 列、sensor_tag列。

输出新增列：
    - label : 最终业务状态（运行/停机/限功率/传感器异常-xxx）

状态优先级（高→低）：传感器异常 > 停机 > 限功率 > 运行
"""
from typing import Dict, Optional

import numpy as np
import pandas as pd

# ==================== 打标参数 ====================

# 功率越界倍数（相对额定功率）
STATUS_POWER_UPPER_RATIO = 1.25  # 功率上限倍数
STATUS_POWER_LOWER_RATIO = -0.10  # 功率下限倍数
STATUS_SHUTDOWN_RATIO = 0.005  # 停机功率阈值（额定功率的比例），最小取 50kW
STATUS_CURTAIL_LOW_RATIO = 0.02  # 限功率下限（额定功率比例）
STATUS_CURTAIL_HIGH_RATIO = 0.95  # 限功率上限（额定功率比例）
STATUS_CURTAIL_PITCH_OFFSET = 3.0  # 限功率桨距角偏移量（基准桨距角 + 此值）

# 风速越界阈值（m/s）
STATUS_WIND_MAX = 75.0
STATUS_WIND_MIN = -2.0

# 变桨越界阈值（°）
STATUS_PITCH_MAX = 105.0
STATUS_PITCH_MIN = -10.0

# 转速/扭矩越界倍数
STATUS_SPD_UPPER_RATIO = 1.25
STATUS_TORQUE_UPPER_RATIO = 1.25
STATUS_TORQUE_LOWER_ABS = -2000.0

# 逻辑悖论阈值
STATUS_LOGIC_WIND_MIN = 0.1  # 风速低于此值但功率大则为逻辑异常
STATUS_LOGIC_POWER_MIN = 100.0  # 配合上面风速阈值的功率下限

# 机型特殊规则（可扩展）
STATUS_MODEL_SPECIAL_RULES = {
    "EN156-3300": [(2.7, 450)],  # 示例：风速 < 2.7 且功率 > 450 时追加逻辑异常
}

# 传感器异常列与对应可读标签（内部使用，不输出）
_SENSOR_COL_LABEL: Dict[str, str] = {
    "d_val_power": "功率值异常",
    "d_val_wind": "风速值异常",
    "d_val_pitch": "变桨值异常",
    "d_val_spd": "转速值异常",
    "d_val_torque": "扭矩值异常",
    "d_logic_wind_pwr": "风速功率逻辑异常",
    "d_logic_torque_pwr": "转速扭矩逻辑异常",
}

# ==================== 阈值加载（静态数据） ====================

# 静态阈值数据（从机型参数阈值表.xlsx转换）
_THRESHOLD_CACHE: Dict[str, dict] = {
    "CCWE1500-82.DF": {
        "p_max_observed": 1554.0,
        "torque_limit": 17924.0,
        "spd_limit": 1861.59,
        "baseline_pitch": 0.04
    },
    "CCWE3000-122.HD": {
        "p_max_observed": 3047.0,
        "torque_limit": None,
        "spd_limit": 362.3,
        "baseline_pitch": 0.19
    },
    "DEW-D10000-185": {
        "p_max_observed": 10135.3,
        "torque_limit": 10841040.0,
        "spd_limit": 10.45,
        "baseline_pitch": -0.5
    },
    "DEW-D3800-148": {
        "p_max_observed": 3508.8,
        "torque_limit": 3320535.25,
        "spd_limit": 11.42,
        "baseline_pitch": 0.0
    },
    "DEW-G3600-165": {
        "p_max_observed": 3730.5,
        "torque_limit": 21010.0,
        "spd_limit": 1838.41,
        "baseline_pitch": 0.15
    },
    "DEW-G5500-183": {
        "p_max_observed": 5548.6,
        "torque_limit": 32636.0,
        "spd_limit": 1746.57,
        "baseline_pitch": 2.85
    },
    "DEW-G6250-186": {
        "p_max_observed": 6314.0,
        "torque_limit": 92110.56,
        "spd_limit": 710.82,
        "baseline_pitch": 0.1
    },
    "DEW-H8350-A-212": {
        "p_max_observed": 7085.1,
        "torque_limit": 125065.52,
        "spd_limit": 8.82,
        "baseline_pitch": 1.89
    },
    "DF103-2000": {
        "p_max_observed": 2024.2,
        "torque_limit": 1264956.0,
        "spd_limit": 16.37,
        "baseline_pitch": -0.5
    },
    "DF131-2500": {
        "p_max_observed": 2527.9,
        "torque_limit": 2058254.75,
        "spd_limit": 13.25,
        "baseline_pitch": 0.0
    },
    "EN141-2650": {
        "p_max_observed": 2744.6,
        "torque_limit": 15705.3,
        "spd_limit": 1778.21,
        "baseline_pitch": -0.79
    },
    "EN156-3300": {
        "p_max_observed": 3359.5,
        "torque_limit": 18575.35,
        "spd_limit": 1835.56,
        "baseline_pitch": -1.45
    },
    "EN171-4500": {
        "p_max_observed": 4559.4,
        "torque_limit": 25390.95,
        "spd_limit": 1795.83,
        "baseline_pitch": -0.85
    },
    "EN182-6250": {
        "p_max_observed": 6326.0,
        "torque_limit": 34601.38,
        "spd_limit": 1802.05,
        "baseline_pitch": -0.25
    },
    "FD77-1500": {
        "p_max_observed": 1534.2,
        "torque_limit": 8058.38,
        "spd_limit": 1846.84,
        "baseline_pitch": 0.0
    },
    "G52-850": {
        "p_max_observed": 851.5,
        "torque_limit": None,
        "spd_limit": 1653.3,
        "baseline_pitch": -2.2
    },
    "G58-850": {
        "p_max_observed": 813.5,
        "torque_limit": None,
        "spd_limit": 1670.4,
        "baseline_pitch": 0.48
    },
    "GW140-2500": {
        "p_max_observed": 2575.0,
        "torque_limit": None,
        "spd_limit": 12.31,
        "baseline_pitch": 3.63
    },
    "GW140-3300": {
        "p_max_observed": 3486.0,
        "torque_limit": None,
        "spd_limit": None,
        "baseline_pitch": 4.25
    },
    "GW191-5000": {
        "p_max_observed": 5004.0,
        "torque_limit": None,
        "spd_limit": 705.6,
        "baseline_pitch": 0.0
    },
    "GW191-6250": {
        "p_max_observed": 6258.6,
        "torque_limit": None,
        "spd_limit": 686.2,
        "baseline_pitch": 2.2
    },
    "GW82-1500": {
        "p_max_observed": 1570.0,
        "torque_limit": None,
        "spd_limit": 18.12,
        "baseline_pitch": 0.2
    },
    "GWH191-5270": {
        "p_max_observed": 4916.6,
        "torque_limit": None,
        "spd_limit": 709.99,
        "baseline_pitch": 0.29
    },
    "GWH191-6700": {
        "p_max_observed": 5905.7,
        "torque_limit": None,
        "spd_limit": 685.9,
        "baseline_pitch": 0.08
    },
    "MY2.0se-121": {
        "p_max_observed": 2079.0,
        "torque_limit": None,
        "spd_limit": 1798.0,
        "baseline_pitch": 0.03
    },
    "MY3.0se-145": {
        "p_max_observed": 3212.0,
        "torque_limit": None,
        "spd_limit": 316.0,
        "baseline_pitch": 0.07
    },
    "MY4.0se-166": {
        "p_max_observed": 4160.0,
        "torque_limit": None,
        "spd_limit": 1095.0,
        "baseline_pitch": -0.45
    },
    "MY5.0se-155": {
        "p_max_observed": 5551.0,
        "torque_limit": None,
        "spd_limit": 284.6,
        "baseline_pitch": 0.0
    },
    "MY5.0se-166": {
        "p_max_observed": 5162.0,
        "torque_limit": 102.4,
        "spd_limit": 1031.1,
        "baseline_pitch": -0.49
    },
    "MY5.2se-166": {
        "p_max_observed": 5242.0,
        "torque_limit": None,
        "spd_limit": None,
        "baseline_pitch": -0.37
    },
    "MY6.25se-172": {
        "p_max_observed": 6346.0,
        "torque_limit": None,
        "spd_limit": 228.6,
        "baseline_pitch": -0.49
    },
    "S48-750": {
        "p_max_observed": 780.0,
        "torque_limit": None,
        "spd_limit": 1524.0,
        "baseline_pitch": 0.0
    },
    "SL1500-82": {
        "p_max_observed": 1544.3,
        "torque_limit": None,
        "spd_limit": 1841.5,
        "baseline_pitch": 0.01
    },
    "SL3000-113": {
        "p_max_observed": 3055.7,
        "torque_limit": None,
        "spd_limit": 1229.26,
        "baseline_pitch": 0.02
    },
    "SWT7000-154": {
        "p_max_observed": 7163.4,
        "torque_limit": None,
        "spd_limit": 10.8,
        "baseline_pitch": -1.69
    },
    "UP1500-82": {
        "p_max_observed": 1502.4,
        "torque_limit": 103.87,
        "spd_limit": 1777.1,
        "baseline_pitch": 0.03
    },
    "UP1500-86": {
        "p_max_observed": 1558.4,
        "torque_limit": 103.53,
        "spd_limit": 1773.6,
        "baseline_pitch": 0.03
    },
    "V100-2000": {
        "p_max_observed": 1999.8,
        "torque_limit": None,
        "spd_limit": 1734.83,
        "baseline_pitch": -2.7
    },
    "V110-2200": {
        "p_max_observed": 2204.1,
        "torque_limit": None,
        "spd_limit": 1757.7,
        "baseline_pitch": 0.9
    },
    "V52-850": {
        "p_max_observed": 854.8,
        "torque_limit": None,
        "spd_limit": 1693.8,
        "baseline_pitch": -1.97
    },
    "V60-850": {
        "p_max_observed": 853.2,
        "torque_limit": None,
        "spd_limit": 1673.9,
        "baseline_pitch": -3.06
    },
    "V80-2000": {
        "p_max_observed": 2043.9,
        "torque_limit": None,
        "spd_limit": 1748.7,
        "baseline_pitch": -1.74
    },
    "V90-2000": {
        "p_max_observed": 2005.7,
        "torque_limit": None,
        "spd_limit": 1730.4,
        "baseline_pitch": -2.16
    },
    "W2000-111": {
        "p_max_observed": 2111.0,
        "torque_limit": 100.04,
        "spd_limit": 1872.0,
        "baseline_pitch": 0.18
    },
    "W2500-135": {
        "p_max_observed": 2566.0,
        "torque_limit": None,
        "spd_limit": 1504.6,
        "baseline_pitch": 0.0
    },
    "W3450-146": {
        "p_max_observed": 3533.0,
        "torque_limit": None,
        "spd_limit": 1604.8,
        "baseline_pitch": 0.2
    },
    "XE122-2500": {
        "p_max_observed": 2568.0,
        "torque_limit": None,
        "spd_limit": 14.32,
        "baseline_pitch": 0.5
    },
    "XE72-2000": {
        "p_max_observed": 2084.0,
        "torque_limit": None,
        "spd_limit": 23.58,
        "baseline_pitch": -0.27
    }
}


def get_model_stats(model_name: str) -> dict:
    """根据机型名称获取对应的阈值字典"""
    thresh = _THRESHOLD_CACHE
    if model_name not in thresh:
        raise ValueError(f"未找到机型 {model_name} 的阈值配置")
    return thresh[model_name]


# ==================== 核心打标函数 ====================

def _label_dataframe(
        df_input: pd.DataFrame,
        stats: Optional[dict] = None,
        model_name: Optional[str] = None
) -> pd.DataFrame:
    """
    内部打标函数,返回包含所有中间列(d_val_* 等)的完整 DataFrame。
    参数含义同 apply_labels。
    """
    if stats is None:
        if model_name is None:
            raise ValueError("必须提供 stats 或 model_name")
        stats = get_model_stats(model_name)

    df = df_input.copy()
    P_MAX = stats["p_max_observed"]
    PITCH_BASE = stats["baseline_pitch"]

    # ── 1. 传感器异常列 ────────────────────────────────────────────────────────
    df["d_val_power"] = (
            (df["p_active"] > P_MAX * STATUS_POWER_UPPER_RATIO) |
            (df["p_active"] < P_MAX * STATUS_POWER_LOWER_RATIO)
    )

    if "wind_spd" in df.columns:
        df["d_val_wind"] = (df["wind_spd"] > STATUS_WIND_MAX) | (df["wind_spd"] < STATUS_WIND_MIN)
    else:
        df["d_val_wind"] = False

    pitch_cols = [c for c in df.columns if "pitch_ang_act" in c]
    df["d_val_pitch"] = False
    for col in pitch_cols:
        df["d_val_pitch"] |= (df[col] > STATUS_PITCH_MAX) | (df[col] < STATUS_PITCH_MIN)

    df["d_val_spd"] = False
    if stats["spd_limit"] and "gen_spd" in df.columns:
        df["d_val_spd"] = (
                (df["gen_spd"] > stats["spd_limit"] * STATUS_SPD_UPPER_RATIO) |
                (df["gen_spd"] < -200)
        )

    df["d_val_torque"] = False
    if stats["torque_limit"] and "actual_torque" in df.columns:
        df["d_val_torque"] = (
                (df["actual_torque"] > stats["torque_limit"] * STATUS_TORQUE_UPPER_RATIO) |
                (df["actual_torque"] < STATUS_TORQUE_LOWER_ABS)
        )

    # ── 2. 逻辑悖论列 ──────────────────────────────────────────────────────────
    if "wind_spd" in df.columns:
        df["d_logic_wind_pwr"] = (
                (df["wind_spd"] < STATUS_LOGIC_WIND_MIN) &
                (df["p_active"] > STATUS_LOGIC_POWER_MIN)
        )
        # 风速冻结检测：滑动窗口内风速几乎不变但功率明显不为0（传感器卡死）
        wind_std = df["wind_spd"].rolling(window=60, min_periods=2).std()
        df["d_logic_wind_pwr"] |= (
                (wind_std < 0.05) &
                (df["p_active"] > STATUS_LOGIC_POWER_MIN)
        )
        # 机型特殊规则
        if model_name is not None:
            for wind_thresh, pwr_thresh in STATUS_MODEL_SPECIAL_RULES.get(model_name, []):
                df["d_logic_wind_pwr"] |= (
                        (df["wind_spd"] < wind_thresh) & (df["p_active"] > pwr_thresh)
                )
    else:
        df["d_logic_wind_pwr"] = False

    df["d_logic_torque_pwr"] = False
    if stats["torque_limit"] and "actual_torque" in df.columns:
        df["d_logic_torque_pwr"] = (
                (df["p_active"] > P_MAX * 0.1) &
                (df["actual_torque"].abs() < stats["torque_limit"] * 0.01)
        )

    # ── 3. 汇总传感器异常标签 ───────────────────────────
    all_sensor_cols = list(_SENSOR_COL_LABEL.keys())
    existing = [c for c in all_sensor_cols if c in df.columns]

    if existing:
        tag_matrix = np.where(
            df[existing].values,
            np.array([_SENSOR_COL_LABEL[c] for c in existing]),
            "",
        )
        df["sensor_anomaly_tags"] = pd.Series(
            [",".join(t for t in row if t) for row in tag_matrix],
            index=df.index,
        )
    else:
        df["sensor_anomaly_tags"] = ""

    # ── 4. 业务状态打标 ────────────────────────────────────────────────────────
    any_sensor_anomaly = df[[c for c in existing if c in df.columns]].any(axis=1)

    df["label"] = "运行"

    shutdown_thresh = max(50.0, P_MAX * STATUS_SHUTDOWN_RATIO)
    mask_shutdown = (~df["d_val_power"]) & (df["p_active"] <= shutdown_thresh)
    df.loc[mask_shutdown, "label"] = "停机"

    if "pitch_ang_act_1" in df.columns:
        mask_curtail = (
                (df["label"] != "停机") &
                (~df["d_val_power"]) &
                (~df["d_val_pitch"]) &
                (df["p_active"] > P_MAX * STATUS_CURTAIL_LOW_RATIO) &
                (df["p_active"] < P_MAX * STATUS_CURTAIL_HIGH_RATIO) &
                (df["pitch_ang_act_1"] > (PITCH_BASE + STATUS_CURTAIL_PITCH_OFFSET))
        )
        df.loc[mask_curtail, "label"] = "限功率"

    # 传感器异常覆盖
    mask_sensor = any_sensor_anomaly
    df.loc[mask_sensor, "label"] = "传感器异常"

    # 新增 sensor_tag 列：7位整数，每位1=正常，2=异常 
    # 7位整数从左至右 分别为功率值异常（最高位）、风速值异常、变桨值异常、转速值异常、扭矩值异常、风速功率逻辑异常、转速扭矩逻辑异常（最低位）
    sensor_order = [
        'd_val_power', 'd_val_wind', 'd_val_pitch', 'd_val_spd', 'd_val_torque', 'd_logic_wind_pwr',
        'd_logic_torque_pwr'
    ]
    # 确保这些列都存在
    tag_str_series = df[sensor_order].apply(
        lambda row: ''.join('2' if x else '1' for x in row), axis=1
    )
    df['sensor_tag'] = tag_str_series.astype(int)

    return df


def apply_labels(
        df: pd.DataFrame,
        model_name: str
) -> pd.DataFrame:
    """
    便捷打标函数：传入原始 DataFrame 和机型名称，返回打标后的 DataFrame。

    返回的 DataFrame 包含原始数据的所有列，以及新增的 `label` 列（最终业务状态）。

    参数
    ----------
    df : pd.DataFrame
        原始数据，必须包含 p_active 列，可选 wind_spd, pitch_ang_act_1, gen_spd, actual_torque 等。
    model_name : str
        机型名称（需与静态数据中的机型名称一致）。

    返回
    -------
    pd.DataFrame
        包含原始数据列 + label 列的 DataFrame。
    """
    # 调用内部打标函数，获取完整结果（包含中间列）
    full_labeled = _label_dataframe(df, model_name=model_name)
    # 只保留原始列和 label 列
    result_cols = list(df.columns) + ['label', 'sensor_tag']
    return full_labeled[result_cols]
