import os
import numpy as np
import gc
import time
import threading
from insightface.app import FaceAnalysis
from service.face_service import get_all_face_name_with_eigenvalue

# GPU状态检查支持
try:
    import pynvml

    pynvml.nvmlInit()
    _nvml_available = True
except ImportError:
    print("警告: pynvml库未安装，无法检测GPU状态，默认尝试使用GPU")
    _nvml_available = False

# 全局人脸引擎与特征库
_face_app = None
_known_faces_embeddings = {}  # 姓名 -> 归一化特征值的映射
_known_faces_names = []  # 已知人脸姓名列表

# GPU使用状态标记
_using_gpu = False  # 是否使用GPU
_used_gpu_id = -1  # 使用的GPU ID（-1表示CPU）

# 资源管理变量
_ref_count = 0  # 引擎引用计数（记录当前使用次数）
# 修复点1：初始值设为当前时间，避免未加载引擎时用0计算超时
_last_used_time = time.time()
_lock = threading.Lock()  # 线程安全锁
_release_timeout = 8  # 闲置超时时间（秒）
_is_releasing = False  # 资源释放中标记
_monitor_thread_running = False  # 监控线程运行标记

# 调试计数器
_debug_counter = {
    "engine_created": 0,  # 引擎创建次数
    "engine_released": 0,  # 引擎释放次数
    "detection_calls": 0  # 检测函数调用次数
}


def check_gpu_availability(gpu_id, memory_threshold=0.7):
    """检查指定GPU的内存使用率是否低于阈值（判定为“可用”）"""
    if not _nvml_available:
        return True
    try:
        handle = pynvml.nvmlDeviceGetHandleByIndex(gpu_id)
        mem_info = pynvml.nvmlDeviceGetMemoryInfo(handle)
        memory_usage = mem_info.used / mem_info.total
        return memory_usage < memory_threshold
    except Exception as e:
        print(f"检查GPU {gpu_id} 状态失败: {e}")
        return False


def select_best_gpu(preferred_gpus=[0, 1]):
    """按优先级选择可用GPU，优先0号；均不可用则返回-1（CPU）"""
    for gpu_id in preferred_gpus:
        try:
            # 验证GPU是否存在
            if _nvml_available:
                pynvml.nvmlDeviceGetHandleByIndex(gpu_id)
            # 验证GPU内存是否充足
            if check_gpu_availability(gpu_id):
                print(f"GPU {gpu_id} 可用，将使用该GPU")
                return gpu_id
            else:
                if gpu_id == 0:
                    print("GPU 0 内存使用率过高，尝试其他GPU")
        except Exception as e:
            print(f"GPU {gpu_id} 不可用或访问失败: {e}")
    print("所有指定GPU均不可用，将使用CPU计算")
    return -1


def _release_engine_resources():
    """释放人脸引擎的所有资源（模型、特征库、GPU缓存等）"""
    global _face_app, _is_releasing, _known_faces_embeddings, _known_faces_names, _last_used_time
    if not _face_app or _is_releasing:
        return

    try:
        _is_releasing = True
        print("开始释放人脸引擎资源...")

        # 释放InsightFace模型资源
        if hasattr(_face_app, "model"):
            _face_app.model = None  # 显式置空模型引用
        _face_app = None  # 释放引擎实例

        # 清空人脸特征库
        _known_faces_embeddings.clear()
        _known_faces_names.clear()

        _debug_counter["engine_released"] += 1
        print(f"人脸引擎已释放，调试统计: {_debug_counter}")

        # 强制垃圾回收
        gc.collect()

        # 清理各深度学习框架的GPU缓存
        # Torch 缓存清理
        try:
            import torch
            if torch.cuda.is_available():
                torch.cuda.empty_cache()
                torch.cuda.ipc_collect()
                print("Torch GPU缓存已清理")
        except ImportError:
            pass

        # TensorFlow 缓存清理
        try:
            import tensorflow as tf
            tf.keras.backend.clear_session()
            print("TensorFlow会话已清理")
        except ImportError:
            pass

        # MXNet 缓存清理（InsightFace底层常用MXNet）
        try:
            import mxnet as mx
            mx.nd.waitall()  # 等待所有计算完成并释放资源
            print("MXNet资源已等待释放")
        except ImportError:
            pass

    except Exception as e:
        print(f"释放资源过程中出错: {e}")
    finally:
        _is_releasing = False
        # 修复点2：释放完成后重置最后使用时间，避免下次加载时复用旧值
        _last_used_time = time.time()


def _resource_monitor_thread():
    """后台监控线程：检测引擎闲置超时，触发资源释放"""
    global _ref_count, _last_used_time, _face_app, _monitor_thread_running
    _monitor_thread_running = True
    while _monitor_thread_running:
        time.sleep(2)  # 缩短检查间隔，加快闲置检测响应
        with _lock:
            # 当“引擎存在 + 无引用 + 未在释放中”时，检查闲置时间
            if _face_app and _ref_count == 0 and not _is_releasing:
                idle_time = time.time() - _last_used_time
                if idle_time > _release_timeout:
                    print(f"引擎闲置超时（{idle_time:.1f}s > {_release_timeout}s），释放资源")
                    _release_engine_resources()


def load_model(prefer_gpu=True, preferred_gpus=[0, 1]):
    """加载人脸识别引擎及已知人脸特征库（默认优先用0号GPU）"""
    global _face_app, _known_faces_embeddings, _known_faces_names, _using_gpu, _used_gpu_id, _last_used_time

    # 启动后台监控线程（确保仅启动一次）
    if not _monitor_thread_running:
        threading.Thread(
            target=_resource_monitor_thread,
            daemon=True,
            name="FaceEngineMonitor"
        ).start()
        print("人脸引擎监控线程已启动")

    # 若正在释放资源，等待释放完成
    while _is_releasing:
        time.sleep(0.1)

    # 若引擎已初始化，直接返回
    if _face_app:
        return True

    # 初始化InsightFace引擎
    try:
        print("正在初始化InsightFace人脸识别引擎...")
        _face_app = FaceAnalysis(name="buffalo_l", root=os.path.expanduser("~/.insightface"))

        # 选择GPU（优先用0号）
        ctx_id = 0
        if prefer_gpu:
            ctx_id = select_best_gpu(preferred_gpus)
            _using_gpu = ctx_id != -1
            _used_gpu_id = ctx_id if _using_gpu else -1

        if _using_gpu:
            print(f"引擎初始化成功，将使用GPU {ctx_id} 计算")
        else:
            print("引擎初始化成功，将使用CPU计算")

        # 准备模型（加载到指定设备）
        _face_app.prepare(ctx_id=ctx_id, det_size=(640, 640))
        print("InsightFace引擎初始化完成")

        # 修复点3：引擎初始化成功后，立即更新“最后使用时间”（核心修复）
        _last_used_time = time.time()

        _debug_counter["engine_created"] += 1
        print(f"引擎调试统计: {_debug_counter}")

    except Exception as e:
        print(f"引擎初始化失败: {e}")
        return False

    # 从服务加载已知人脸的姓名和特征值
    try:
        face_data = get_all_face_name_with_eigenvalue()
        for person_name, eigenvalue_data in face_data.items():
            # 兼容“numpy数组”和“字符串”格式的特征值
            if isinstance(eigenvalue_data, np.ndarray):
                eigenvalue = eigenvalue_data.astype(np.float32)
            elif isinstance(eigenvalue_data, str):
                # 清理字符串中的括号、换行等干扰符
                cleaned = eigenvalue_data.replace("[", "").replace("]", "").replace("\n", "").strip()
                # 分割并转换为浮点数数组
                values = [v for v in cleaned.split() if v]  # 兼容空格/逗号分隔
                eigenvalue = np.array(list(map(float, values)), dtype=np.float32)
            else:
                print(f"不支持的特征值类型（{type(eigenvalue_data)}），跳过 {person_name}")
                continue

            # 特征值归一化（保证后续相似度计算的一致性）
            norm = np.linalg.norm(eigenvalue)
            if norm != 0:
                eigenvalue = eigenvalue / norm

            _known_faces_embeddings[person_name] = eigenvalue
            _known_faces_names.append(person_name)

        print(f"成功加载 {len(_known_faces_names)} 个人脸的特征库")

    except Exception as e:
        print(f"加载人脸特征库失败: {e}")

    return _face_app is not None


def detect(frame, similarity_threshold=0.4):
    """
    检测并识别人脸
    返回：(是否匹配到已知人脸, 结果描述字符串)
    """
    global _face_app, _known_faces_embeddings, _known_faces_names, _ref_count, _last_used_time

    # 校验输入帧有效性
    if frame is None or frame.size == 0:
        return (False, "无效的输入帧数据")

    # 加锁并更新引用计数、最后使用时间
    engine = None
    with _lock:
        _ref_count += 1
        _last_used_time = time.time()
        _debug_counter["detection_calls"] += 1

        # 若引擎未初始化且未在释放中，尝试初始化
        if not _face_app and not _is_releasing:
            if not load_model(prefer_gpu=True):
                # 初始化失败，恢复引用计数
                with _lock:
                    _ref_count = max(0, _ref_count - 1)
                return (False, "人脸引擎初始化失败")

        engine = _face_app  # 获取引擎引用

    # 校验引擎可用性
    if not engine or len(_known_faces_names) == 0:
        with _lock:
            _ref_count = max(0, _ref_count - 1)
        return (False, "人脸引擎不可用或特征库为空")

    try:
        # GPU计算时，确保帧数据是连续内存（避免CUDA错误）
        if _using_gpu and engine is not None and not frame.flags.contiguous:
            frame = np.ascontiguousarray(frame)

        # 执行人脸检测与特征提取
        faces = engine.get(frame)
    except Exception as e:
        print(f"人脸检测过程出错: {e}")
        # 出错时尝试重新初始化引擎（可能是GPU状态变化导致）
        print("尝试重新初始化人脸引擎...")
        with _lock:
            _ref_count = max(0, _ref_count - 1)
        load_model(prefer_gpu=True)
        return (False, f"检测错误: {str(e)}")

    result_parts = []
    has_matched_known_face = False  # 是否有任意人脸匹配到已知库

    for face in faces:
        # 归一化当前检测到的人脸特征
        face_embedding = face.embedding.astype(np.float32)
        norm = np.linalg.norm(face_embedding)
        if norm == 0:
            continue
        face_embedding = face_embedding / norm

        # 与已知人脸特征逐一比对
        max_similarity, best_match_name = -1.0, "Unknown"
        for name in _known_faces_names:
            known_emb = _known_faces_embeddings[name]
            similarity = np.dot(face_embedding, known_emb)  # 余弦相似度
            if similarity > max_similarity:
                max_similarity = similarity
                best_match_name = name

        # 判断是否匹配成功
        is_matched = max_similarity >= similarity_threshold
        if is_matched:
            has_matched_known_face = True

        # 记录该人脸的检测结果
        bbox = face.bbox  # 人脸边界框
        result_parts.append(
            f"{'匹配' if is_matched else '未匹配'}: {best_match_name} "
            f"(相似度: {max_similarity:.2f}, 边界框: {bbox.astype(int).tolist()})"
        )

    # 构建最终结果字符串
    result_str = "未检测到人脸" if not result_parts else "; ".join(result_parts)

    # 释放引用计数（线程安全）
    with _lock:
        _ref_count = max(0, _ref_count - 1)
        # 若仍有引用，更新最后使用时间；若引用为0，也立即标记（加快闲置检测）
        _last_used_time = time.time()

    return (has_matched_known_face, result_str)