from typing import Dict, Any
from app import db
from datetime import datetime
import os
import boto3
from botocore.config import Config
import logging
import requests
import json
import re
import uuid
from PIL import Image
from io import BytesIO
import pytesseract
import base64
from openai import OpenAI
from app.config.config import DevelopmentConfig, ProductionConfig
import time  # 添加导入时间模块

# 导入Neo4j相关函数
from app.core.data_parse.parse_task import create_or_get_talent_node, process_career_path
from app.core.data_parse.time_utils import get_east_asia_time_naive

# 名片解析数据模型
class BusinessCard(db.Model):
    __tablename__ = 'business_cards'
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name_zh = db.Column(db.String(100), nullable=False)
    name_en = db.Column(db.String(100))
    title_zh = db.Column(db.String(100))
    title_en = db.Column(db.String(100))
    mobile = db.Column(db.String(100))
    phone = db.Column(db.String(50))
    email = db.Column(db.String(100))
    hotel_zh = db.Column(db.String(200))
    hotel_en = db.Column(db.String(200))
    address_zh = db.Column(db.Text)
    address_en = db.Column(db.Text)
    postal_code_zh = db.Column(db.String(20))
    postal_code_en = db.Column(db.String(20))
    brand_zh = db.Column(db.String(100))
    brand_en = db.Column(db.String(100))
    affiliation_zh = db.Column(db.String(200))
    affiliation_en = db.Column(db.String(200))
    birthday = db.Column(db.Date)  # 生日，存储年月日
    age = db.Column(db.Integer)  # 年龄字段
    native_place = db.Column(db.Text)  # 籍贯字段
    gender = db.Column(db.String(10))  # 新增性别字段
    residence = db.Column(db.Text)  # 居住地
    image_path = db.Column(db.String(255))  # MinIO中存储的路径
    career_path = db.Column(db.JSON)  # 职业轨迹，JSON格式
    brand_group = db.Column(db.String(200))  # 品牌组合
    origin_source = db.Column(db.JSON)  # 原始资料记录，JSON格式
    talent_profile = db.Column(db.Text)  # 人才档案，文本格式
    created_at = db.Column(db.DateTime, default=get_east_asia_time_naive, nullable=False)
    updated_at = db.Column(db.DateTime, onupdate=get_east_asia_time_naive)
    updated_by = db.Column(db.String(50))
    status = db.Column(db.String(20), default='active')
    
    def to_dict(self):
        return {
            'id': self.id,
            'name_zh': self.name_zh,
            'name_en': self.name_en,
            'title_zh': self.title_zh,
            'title_en': self.title_en,
            'mobile': self.mobile,
            'phone': self.phone,
            'email': self.email,
            'hotel_zh': self.hotel_zh,
            'hotel_en': self.hotel_en,
            'address_zh': self.address_zh,
            'address_en': self.address_en,
            'postal_code_zh': self.postal_code_zh,
            'postal_code_en': self.postal_code_en,
            'brand_zh': self.brand_zh,
            'brand_en': self.brand_en,
            'affiliation_zh': self.affiliation_zh,
            'affiliation_en': self.affiliation_en,
            'birthday': self.birthday.strftime('%Y-%m-%d') if self.birthday else None,
            'age': self.age,
            'native_place': self.native_place,
            'gender': self.gender,  # 新增性别字段
            'residence': self.residence,
            'image_path': self.image_path,
            'career_path': self.career_path,
            'brand_group': self.brand_group,
            'origin_source': self.origin_source,
            'talent_profile': self.talent_profile,
            'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else None,
            'updated_at': self.updated_at.strftime('%Y-%m-%d %H:%M:%S') if self.updated_at else None,
            'updated_by': self.updated_by,
            'status': self.status
        }


# 解析人才数据模型
class ParsedTalent(db.Model):
    __tablename__ = 'parsed_talents'
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name_zh = db.Column(db.String(100), nullable=False)
    name_en = db.Column(db.String(100))
    title_zh = db.Column(db.String(100))
    title_en = db.Column(db.String(100))
    mobile = db.Column(db.String(50))
    phone = db.Column(db.String(50))
    email = db.Column(db.String(100))
    hotel_zh = db.Column(db.String(200))
    hotel_en = db.Column(db.String(200))
    address_zh = db.Column(db.Text)
    address_en = db.Column(db.Text)
    postal_code_zh = db.Column(db.String(20))
    postal_code_en = db.Column(db.String(20))
    brand_zh = db.Column(db.String(100))
    brand_en = db.Column(db.String(100))
    affiliation_zh = db.Column(db.String(200))
    affiliation_en = db.Column(db.String(200))
    image_path = db.Column(db.String(255))
    career_path = db.Column(db.JSON)
    brand_group = db.Column(db.String(200))
    created_at = db.Column(db.DateTime, default=get_east_asia_time_naive, nullable=False)
    updated_at = db.Column(db.DateTime, onupdate=get_east_asia_time_naive)
    updated_by = db.Column(db.String(50))
    status = db.Column(db.String(20), default='active')
    birthday = db.Column(db.Date)
    residence = db.Column(db.Text)
    age = db.Column(db.Integer)
    native_place = db.Column(db.Text)
    gender = db.Column(db.String(10))  # 新增性别字段
    origin_source = db.Column(db.JSON)
    talent_profile = db.Column(db.Text)
    task_id = db.Column(db.String(50))
    task_type = db.Column(db.String(20))
    
    def to_dict(self):
        return {
            'id': self.id,
            'name_zh': self.name_zh,
            'name_en': self.name_en,
            'title_zh': self.title_zh,
            'title_en': self.title_en,
            'mobile': self.mobile,
            'phone': self.phone,
            'email': self.email,
            'hotel_zh': self.hotel_zh,
            'hotel_en': self.hotel_en,
            'address_zh': self.address_zh,
            'address_en': self.address_en,
            'postal_code_zh': self.postal_code_zh,
            'postal_code_en': self.postal_code_en,
            'brand_zh': self.brand_zh,
            'brand_en': self.brand_en,
            'affiliation_zh': self.affiliation_zh,
            'affiliation_en': self.affiliation_en,
            'image_path': self.image_path,
            'career_path': self.career_path,
            'brand_group': self.brand_group,
            'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else None,
            'updated_at': self.updated_at.strftime('%Y-%m-%d %H:%M:%S') if self.updated_at else None,
            'updated_by': self.updated_by,
            'status': self.status,
            'birthday': self.birthday.strftime('%Y-%m-%d') if self.birthday else None,
            'residence': self.residence,
            'age': self.age,
            'native_place': self.native_place,
            'gender': self.gender,  # 新增性别字段
            'origin_source': self.origin_source,
            'talent_profile': self.talent_profile,
            'task_id': self.task_id,
            'task_type': self.task_type
        }


# 重复名片处理数据模型
class DuplicateBusinessCard(db.Model):
    __tablename__ = 'duplicate_business_cards'
    
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    main_card_id = db.Column(db.Integer, db.ForeignKey('business_cards.id'), nullable=False)  # 新创建的主记录ID
    suspected_duplicates = db.Column(db.JSON, nullable=False)  # 疑似重复记录列表，JSON格式
    duplicate_reason = db.Column(db.String(200), nullable=False)  # 重复原因
    processing_status = db.Column(db.String(20), default='pending')  # 处理状态：pending/processed/ignored
    created_at = db.Column(db.DateTime, default=get_east_asia_time_naive, nullable=False)
    processed_at = db.Column(db.DateTime)  # 处理时间
    processed_by = db.Column(db.String(50))  # 处理人
    processing_notes = db.Column(db.Text)  # 处理备注
    
    # 关联主记录
    main_card = db.relationship('BusinessCard', backref=db.backref('as_main_duplicate_records', lazy=True))
    
    def to_dict(self):
        return {
            'id': self.id,
            'main_card_id': self.main_card_id,
            'suspected_duplicates': self.suspected_duplicates,
            'duplicate_reason': self.duplicate_reason,
            'processing_status': self.processing_status,
            'created_at': self.created_at.strftime('%Y-%m-%d %H:%M:%S') if self.created_at else None,
            'processed_at': self.processed_at.strftime('%Y-%m-%d %H:%M:%S') if self.processed_at else None,
            'processed_by': self.processed_by,
            'processing_notes': self.processing_notes
        }





# 配置变量
# 使用配置变量,缺省认为在生产环境运行
config = ProductionConfig()
# 使用配置变量
minio_url = f"{'https' if getattr(config, 'MINIO_SECURE', False) else 'http'}://{getattr(config, 'MINIO_HOST', 'localhost')}"
minio_access_key = getattr(config, 'MINIO_USER', 'minioadmin')
minio_secret_key = getattr(config, 'MINIO_PASSWORD', 'minioadmin')
minio_bucket = getattr(config, 'MINIO_BUCKET', 'dataops')
use_ssl = getattr(config, 'MINIO_SECURE', False)

# API密钥配置
QWEN_API_KEY = getattr(config, 'QWEN_API_KEY', '')
QWEN_API_URL = getattr(config, 'QWEN_API_URL', 'https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation')

# Qwen文本API配置（替代Deepseek）
QWEN_TEXT_API_KEY = getattr(config, 'QWEN_TEXT_API_KEY', '')
QWEN_TEXT_BASE_URL = getattr(config, 'QWEN_TEXT_BASE_URL', 'https://dashscope.aliyuncs.com/compatible-mode/v1')
QWEN_TEXT_MODEL = getattr(config, 'QWEN_TEXT_MODEL', 'qwen-turbo')

# OCR配置
OCR_LANG = getattr(config, 'OCR_LANG', 'chi_sim+eng')

# 名片解析功能模块

def normalize_mobile_numbers(mobile_str):
    """
    标准化手机号码字符串，去重并限制最多3个
    
    Args:
        mobile_str (str): 手机号码字符串，可能包含多个手机号码，用逗号分隔
        
    Returns:
        str: 标准化后的手机号码字符串，最多3个，用逗号分隔
    """
    if not mobile_str or not mobile_str.strip():
        return ''
    
    # 按逗号分割并清理每个手机号码
    mobiles = []
    for mobile in mobile_str.split(','):
        mobile = mobile.strip()
        if mobile and mobile not in mobiles:  # 去重
            mobiles.append(mobile)
    
    # 限制最多3个手机号码
    return ','.join(mobiles[:3])


def mobile_numbers_overlap(mobile1, mobile2):
    """
    检查两个手机号码字符串是否有重叠
    
    Args:
        mobile1 (str): 第一个手机号码字符串
        mobile2 (str): 第二个手机号码字符串
        
    Returns:
        bool: 是否有重叠的手机号码
    """
    if not mobile1 or not mobile2:
        return False
    
    mobiles1 = set(mobile.strip() for mobile in mobile1.split(',') if mobile.strip())
    mobiles2 = set(mobile.strip() for mobile in mobile2.split(',') if mobile.strip())
    
    return bool(mobiles1 & mobiles2)  # 检查交集


def merge_mobile_numbers(existing_mobile, new_mobile):
    """
    合并手机号码，去重并限制最多3个
    
    Args:
        existing_mobile (str): 现有手机号码字符串
        new_mobile (str): 新手机号码字符串
        
    Returns:
        str: 合并后的手机号码字符串，最多3个，用逗号分隔
    """
    mobiles = []
    
    # 添加现有手机号码
    if existing_mobile:
        for mobile in existing_mobile.split(','):
            mobile = mobile.strip()
            if mobile and mobile not in mobiles:
                mobiles.append(mobile)
    
    # 添加新手机号码
    if new_mobile:
        for mobile in new_mobile.split(','):
            mobile = mobile.strip()
            if mobile and mobile not in mobiles:
                mobiles.append(mobile)
    
    # 限制最多3个手机号码
    return ','.join(mobiles[:3])


def check_duplicate_business_card(extracted_data):
    """
    检查是否存在重复的名片记录
    
    Args:
        extracted_data (dict): 提取的名片信息
        
    Returns:
        dict: 包含检查结果的字典，格式为：
            {
                'is_duplicate': bool,
                'action': str,  # 'update', 'create_with_duplicates' 或 'create_new'
                'existing_card': BusinessCard 或 None,
                'suspected_duplicates': list,  # 疑似重复记录列表
                'reason': str
            }
    """
    try:
        # 提取关键信息进行匹配
        name_zh = extracted_data.get('name_zh', '').strip()
        mobile = extracted_data.get('mobile', '').strip()
        
        # 如果没有姓名，无法进行有效的重复检测
        if not name_zh:
            return {
                'is_duplicate': False,
                'action': 'create_new',
                'existing_card': None,
                'suspected_duplicates': [],
                'reason': '缺少中文姓名，无法进行重复检测'
            }
        
        # 根据姓名进行精确匹配
        name_matches = BusinessCard.query.filter_by(name_zh=name_zh).all()
        
        # 如果有手机号，同时检查手机号匹配
        mobile_matches = []
        if mobile:
            # 标准化手机号进行比较
            normalized_mobile = normalize_mobile_numbers(mobile)
            if normalized_mobile:
                # 查找所有有手机号的记录
                all_cards_with_mobile = BusinessCard.query.filter(BusinessCard.mobile.isnot(None)).all()
                for card in all_cards_with_mobile:
                    if card.mobile and mobile_numbers_overlap(normalized_mobile, card.mobile):
                        mobile_matches.append(card)
        
        # 合并姓名匹配和手机号匹配的结果，去重
        all_matches = []
        for card in name_matches + mobile_matches:
            if card not in all_matches:
                all_matches.append(card)
        
        if not all_matches:
            # 没有找到匹配记录，创建新记录
            return {
                'is_duplicate': False,
                'action': 'create_new',
                'existing_card': None,
                'suspected_duplicates': [],
                'reason': '未找到重复记录，将创建新记录'
            }
        
        elif len(all_matches) == 1:
            # 找到一个匹配记录
            existing_card = all_matches[0]
            
            # 检查是否是完全匹配（姓名和手机号都相同）
            existing_mobile = existing_card.mobile or ''
            is_name_match = existing_card.name_zh == name_zh
            is_mobile_match = mobile and mobile_numbers_overlap(mobile, existing_mobile)
            
            if is_name_match and is_mobile_match:
                # 完全匹配，更新现有记录
                return {
                    'is_duplicate': True,
                    'action': 'update',
                    'existing_card': existing_card,
                    'suspected_duplicates': [],
                    'reason': f'找到完全匹配的记录 (ID: {existing_card.id})，将更新现有记录'
                }
            else:
                # 部分匹配，标记为疑似重复
                return {
                    'is_duplicate': True,
                    'action': 'create_with_duplicates',
                    'existing_card': None,
                    'suspected_duplicates': [existing_card],
                    'reason': f'找到疑似重复记录 (ID: {existing_card.id})，将创建新记录并标记重复'
                }
        
        else:
            # 找到多个匹配记录，标记为疑似重复
            return {
                'is_duplicate': True,
                'action': 'create_with_duplicates',
                'existing_card': None,
                'suspected_duplicates': all_matches,
                'reason': f'找到 {len(all_matches)} 个疑似重复记录，将创建新记录并标记重复'
            }
    
    except Exception as e:
        logging.error(f"重复检测过程中发生错误: {str(e)}", exc_info=True)
        # 出错时默认创建新记录
        return {
            'is_duplicate': False,
            'action': 'create_new',
            'existing_card': None,
            'suspected_duplicates': [],
                         'reason': f'重复检测失败: {str(e)}，将创建新记录'
         }


def update_career_path(existing_card, new_data):
    """
    合并new_data中的career_path到existing_card的career_path中
    
    Args:
        existing_card: 现有的名片记录对象
        new_data (dict): 新的数据，包含career_path字段
        
    Returns:
        list: 合并后的职业轨迹列表
    """
    try:
        # 获取现有的职业轨迹，如果没有则初始化为空列表
        existing_career_path = existing_card.career_path if existing_card.career_path else []
        
        # 确保existing_career_path是列表格式
        if not isinstance(existing_career_path, list):
            existing_career_path = []
        
        # 获取new_data中的career_path
        new_career_path = new_data.get('career_path', [])
        
        # 确保new_career_path是列表格式
        if not isinstance(new_career_path, list):
            new_career_path = []
        
        # 合并两个career_path列表
        merged_career_path = existing_career_path + new_career_path
        
        # 去重：基于关键字段去重，保留最新的记录
        unique_career_path = []
        seen_entries = set()
        
        for entry in merged_career_path:
            if isinstance(entry, dict):
                # 创建唯一标识符，基于关键字段
                key_fields = (
                    entry.get('hotel_zh', ''),
                    entry.get('hotel_en', ''),
                    entry.get('title_zh', ''),
                    entry.get('title_en', '')
                )
                
                if key_fields not in seen_entries:
                    seen_entries.add(key_fields)
                    unique_career_path.append(entry)
        
        # 限制职业轨迹记录数量（最多保留10条）
        if len(unique_career_path) > 10:
            unique_career_path = unique_career_path[-10:]
            
        return unique_career_path
        
    except Exception as e:
        logging.error(f"合并职业轨迹失败: {str(e)}", exc_info=True)
        # 出错时返回原有的职业轨迹
        return existing_card.career_path if existing_card.career_path else []


def create_main_card_with_duplicates(extracted_data, minio_path, suspected_duplicates, reason, task_type=None):
    """
    创建主名片记录并标记疑似重复记录
    
    Args:
        extracted_data (dict): 提取的名片信息
        minio_path (str): MinIO中的图片路径
        suspected_duplicates (list): 疑似重复的名片记录列表
        reason (str): 重复原因描述
        task_type (str, optional): 任务类型，用于origin_source字段
        
    Returns:
        tuple: (BusinessCard, DuplicateBusinessCard) 创建的主名片记录和重复记录标记
    """
    try:
        # 标准化手机号码
        mobile = normalize_mobile_numbers(extracted_data.get('mobile', ''))
        
        # 直接使用extracted_data中的career_path记录
        career_path = extracted_data.get('career_path', [])
        
        # 确定task_type，如果未提供则从extracted_data中获取，否则使用默认值
        if not task_type:
            task_type = extracted_data.get('task_type', '名片')
        
        # 创建新的主名片记录
        main_card = BusinessCard()
        main_card.name_zh = extracted_data.get('name_zh', '')
        main_card.name_en = extracted_data.get('name_en', '')
        main_card.title_zh = extracted_data.get('title_zh', '')
        main_card.title_en = extracted_data.get('title_en', '')
        main_card.mobile = mobile
        main_card.phone = extracted_data.get('phone', '')
        main_card.email = extracted_data.get('email', '')
        main_card.hotel_zh = extracted_data.get('hotel_zh', '')
        main_card.hotel_en = extracted_data.get('hotel_en', '')
        main_card.address_zh = extracted_data.get('address_zh', '')
        main_card.address_en = extracted_data.get('address_en', '')
        main_card.postal_code_zh = extracted_data.get('postal_code_zh', '')
        main_card.postal_code_en = extracted_data.get('postal_code_en', '')
        main_card.brand_zh = extracted_data.get('brand_zh', '')
        main_card.brand_en = extracted_data.get('brand_en', '')
        main_card.affiliation_zh = extracted_data.get('affiliation_zh', '')
        main_card.affiliation_en = extracted_data.get('affiliation_en', '')
        main_card.brand_group = extracted_data.get('brand_group', '')
        main_card.gender = extracted_data.get('gender', '')  # 新增性别字段
        main_card.image_path = minio_path
        main_card.career_path = career_path
        main_card.origin_source = [create_origin_source_entry(task_type, minio_path)]
        main_card.created_at = datetime.now()
        main_card.updated_by = 'system'
        main_card.status = 'duplicate'
        
        # 保存主记录到数据库
        db.session.add(main_card)
        db.session.flush()  # 获取主记录的ID
        
        # 创建重复记录标记
        suspected_duplicates_data = []
        for duplicate_card in suspected_duplicates:
            suspected_duplicates_data.append({
                'id': duplicate_card.id,
                'name_zh': duplicate_card.name_zh,
                'mobile': duplicate_card.mobile,
                'hotel_zh': duplicate_card.hotel_zh,
                'title_zh': duplicate_card.title_zh
            })
        
        duplicate_record = DuplicateBusinessCard()
        duplicate_record.main_card_id = main_card.id
        duplicate_record.suspected_duplicates = suspected_duplicates_data
        duplicate_record.duplicate_reason = reason
        duplicate_record.processing_status = 'pending'
        duplicate_record.created_at = datetime.now()
        
        # 保存重复记录标记
        db.session.add(duplicate_record)
        db.session.commit()
        
        logging.info(f"成功创建主名片记录 ID: {main_card.id}，并标记 {len(suspected_duplicates)} 个疑似重复记录")
        
        return main_card, duplicate_record
        
    except Exception as e:
        db.session.rollback()
        error_msg = f"创建主名片记录失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        raise Exception(error_msg)


def get_minio_client():
    """获取MinIO客户端连接"""
    try:
        # 使用全局配置变量
        global minio_url, minio_access_key, minio_secret_key, minio_bucket, use_ssl
        
        logging.info(f"尝试连接MinIO服务器: {minio_url}")
        
        minio_client = boto3.client(
            's3',
            endpoint_url=minio_url,
            aws_access_key_id=minio_access_key,
            aws_secret_access_key=minio_secret_key,
            config=Config(
                signature_version='s3v4',
                retries={'max_attempts': 3, 'mode': 'standard'},
                connect_timeout=10,
                read_timeout=30
            )
        )
        
        # 确保存储桶存在
        buckets = minio_client.list_buckets()
        bucket_names = [bucket['Name'] for bucket in buckets.get('Buckets', [])]
        logging.info(f"成功连接到MinIO服务器，现有存储桶: {bucket_names}")
        
        if minio_bucket not in bucket_names:
            logging.info(f"创建存储桶: {minio_bucket}")
            minio_client.create_bucket(Bucket=minio_bucket)
            
        return minio_client
    except Exception as e:
        logging.error(f"MinIO连接错误: {str(e)}")
        return None


def get_business_cards():
    """
    获取所有名片记录，并为每个记录添加tag_count字段
    只返回status为active和inactive的记录
    
    Returns:
        dict: 包含名片记录列表的字典
    """
    try:
        # 查询所有名片记录，只返回status为active和inactive的记录，按创建时间倒序排列
        cards = BusinessCard.query.filter(
            BusinessCard.status.in_(['active', 'inactive'])
        ).order_by(BusinessCard.created_at.desc()).all()
        
        # 转换为字典格式
        cards_data = [card.to_dict() for card in cards]
        
        # 从Neo4j图数据库获取每个名片对应的关系数量
        try:
            from app.services.neo4j_driver import neo4j_driver
            
            # 构建批量查询的Cypher语句，获取所有Talent节点的关系数量
            # 只查询BELONGS_TO和WORK_AS这两种关系
            cypher_query = """
            MATCH (t:Talent)-[r:BELONGS_TO|WORK_AS]-()
            WHERE t.pg_id IS NOT NULL
            RETURN t.pg_id as pg_id, count(r) as relation_count
            """
            
            # 执行查询获取关系数量映射
            relation_counts = {}
            with neo4j_driver.get_session() as session:
                result = session.run(cypher_query)
                for record in result:
                    pg_id = record['pg_id']
                    relation_count = record['relation_count']
                    relation_counts[pg_id] = relation_count
            
            # 为每个名片记录添加tag_count字段
            for card_data in cards_data:
                card_id = card_data.get('id')
                if card_id and card_id in relation_counts:
                    card_data['tag_count'] = relation_counts[card_id]
                else:
                    card_data['tag_count'] = 0
                    
        except Exception as neo4j_error:
            logging.warning(f"从Neo4j获取关系数量失败: {str(neo4j_error)}")
            # 如果Neo4j查询失败，为所有记录设置tag_count为0
            for card_data in cards_data:
                card_data['tag_count'] = 0
        
        return {
            'code': 200,
            'success': True,
            'message': '获取名片列表成功',
            'data': cards_data,
            'count': len(cards_data)
        }
    
    except Exception as e:
        error_msg = f"获取名片列表失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': [],
            'count': 0
        }


def get_business_card(card_id):
    """
    根据ID获取单个名片记录
    
    Args:
        card_id (int): 名片记录ID
        
    Returns:
        dict: 包含名片记录的字典
    """
    try:
        card = BusinessCard.query.get(card_id)
        
        if not card:
            return {
                'code': 404,
                'success': False,
                'message': f'未找到ID为{card_id}的名片记录',
                'data': None
            }
        
        return {
            'code': 200,
            'success': True,
            'message': '获取名片记录成功',
            'data': card.to_dict()
        }
    
    except Exception as e:
        error_msg = f"获取名片记录失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }


def update_business_card(card_id, data):
    """
    更新名片记录
    
    Args:
        card_id (int): 名片记录ID
        data (dict): 要更新的数据
        
    Returns:
        dict: 包含更新结果的字典
    """
    try:
        card = BusinessCard.query.get(card_id)
        
        if not card:
            return {
                'code': 404,
                'success': False,
                'message': f'未找到ID为{card_id}的名片记录',
                'data': None
            }
        
        # 更新字段
        updatable_fields = ['name_zh', 'name_en', 'title_zh', 'title_en', 'mobile', 'phone', 'email',
                           'hotel_zh', 'hotel_en', 'address_zh', 'address_en', 'postal_code_zh', 'postal_code_en',
                           'brand_zh', 'brand_en', 'affiliation_zh', 'affiliation_en', 'career_path', 'brand_group', 
                           'birthday', 'residence', 'age', 'native_place', 'gender', 'talent_profile']
        
        for field in updatable_fields:
            if field in data and data[field] is not None:
                setattr(card, field, data[field])
        
        # 处理手机号标准化
        if 'mobile' in data:
            card.mobile = normalize_mobile_numbers(data['mobile'])
        
        card.updated_at = datetime.now()
        card.updated_by = data.get('updated_by', 'system')
        
        db.session.commit()
        
        return {
            'code': 200,
            'success': True,
            'message': '名片记录更新成功',
            'data': card.to_dict()
        }
    
    except Exception as e:
        db.session.rollback()
        error_msg = f"更新名片记录失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }


def update_business_card_status(card_id, status):
    """
    更新名片记录状态
    
    Args:
        card_id (int): 名片记录ID
        status (str): 新的状态
        
    Returns:
        dict: 包含更新结果的字典
    """
    try:
        card = BusinessCard.query.get(card_id)
        
        if not card:
            return {
                'code': 404,
                'success': False,
                'message': f'未找到ID为{card_id}的名片记录',
                'data': None
            }
        
        card.status = status
        card.updated_at = datetime.now()
        
        db.session.commit()
        
        return {
            'code': 200,
            'success': True,
            'message': '名片状态更新成功',
            'data': card.to_dict()
        }
    
    except Exception as e:
        db.session.rollback()
        error_msg = f"更新名片状态失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }


def search_business_cards_by_mobile(mobile_number):
    """
    根据手机号搜索名片记录
    
    Args:
        mobile_number (str): 手机号码
        
    Returns:
        dict: 包含搜索结果的字典
    """
    try:
        if not mobile_number or not mobile_number.strip():
            return {
                'code': 400,
                'success': False,
                'message': '手机号码不能为空',
                'data': [],
                'count': 0
            }
        
        # 查询包含该手机号的记录
        cards = BusinessCard.query.filter(
            BusinessCard.mobile.contains(mobile_number.strip())
        ).all()
        
        # 转换为字典格式
        cards_data = [card.to_dict() for card in cards]
        
        return {
            'code': 200,
            'success': True,
            'message': f'找到 {len(cards_data)} 条匹配记录',
            'data': cards_data,
            'count': len(cards_data)
        }
    
    except Exception as e:
        error_msg = f"搜索名片记录失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': [],
            'count': 0
        }


# 重复记录管理函数
def get_duplicate_records(status=None):
    """
    获取重复记录列表
    
    Args:
        status (str, optional): 筛选特定状态的记录
        
    Returns:
        dict: 包含操作结果和重复记录列表
    """
    try:
        query = DuplicateBusinessCard.query
        if status:
            query = query.filter_by(processing_status=status)
        
        duplicate_records = query.order_by(DuplicateBusinessCard.created_at.desc()).all()
        
        records_data = []
        for record in duplicate_records:
            record_dict = record.to_dict()
            if record.main_card:
                record_dict['main_card'] = record.main_card.to_dict()
            records_data.append(record_dict)
        
        return {
            'code': 200,
            'success': True,
            'message': '获取重复记录列表成功',
            'data': records_data,
            'count': len(records_data)
        }
    
    except Exception as e:
        error_msg = f"获取重复记录列表失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': [],
            'count': 0
        }


def process_duplicate_record(duplicate_id, action, selected_duplicate_id=None, processed_by=None, notes=None):
    """
    处理重复记录
    
    Args:
        duplicate_id (int): 重复记录ID（主记录ID）
        action (str): 处理动作
        selected_duplicate_id (int, optional): 选择的重复记录ID
        processed_by (str, optional): 处理人
        notes (str, optional): 处理备注
        
    Returns:
        dict: 包含操作结果
    """
    try:
        duplicate_record = DuplicateBusinessCard.query.filter_by(main_card_id=duplicate_id).first()
        if not duplicate_record:
            return {
                'code': 404,
                'success': False,
                'message': f'未找到main_card_id为{duplicate_id}的重复记录',
                'data': None
            }
        
        if duplicate_record.processing_status != 'pending':
            return {
                'code': 400,
                'success': False,
                'message': f'重复记录状态为{duplicate_record.processing_status}，无法处理',
                'data': None
            }
        
        main_card = duplicate_record.main_card
        if not main_card:
            return {
                'code': 404,
                'success': False,
                'message': '未找到对应的主记录',
                'data': None
            }
        
        result_data = None
        
        if action == 'merge_to_suspected':
            if not selected_duplicate_id:
                return {
                    'code': 400,
                    'success': False,
                    'message': '执行合并操作时必须提供selected_duplicate_id',
                    'data': None
                }
            
            target_card = BusinessCard.query.get(selected_duplicate_id)
            if not target_card:
                return {
                    'code': 404,
                    'success': False,
                    'message': f'未找到ID为{selected_duplicate_id}的目标记录',
                    'data': None
                }
            
            # 合并信息到目标记录
            target_card.name_en = main_card.name_en or target_card.name_en
            target_card.title_zh = main_card.title_zh or target_card.title_zh
            target_card.title_en = main_card.title_en or target_card.title_en
            
            if main_card.mobile:
                target_card.mobile = merge_mobile_numbers(target_card.mobile, main_card.mobile)
            
            target_card.phone = main_card.phone or target_card.phone
            target_card.email = main_card.email or target_card.email
            target_card.hotel_zh = main_card.hotel_zh or target_card.hotel_zh
            target_card.hotel_en = main_card.hotel_en or target_card.hotel_en
            target_card.address_zh = main_card.address_zh or target_card.address_zh
            target_card.address_en = main_card.address_en or target_card.address_en
            target_card.brand_group = main_card.brand_group or target_card.brand_group
            target_card.image_path = main_card.image_path
            target_card.updated_by = processed_by or 'system'
            
            new_data = {
                'hotel_zh': main_card.hotel_zh,
                'hotel_en': main_card.hotel_en,
                'title_zh': main_card.title_zh,
                'title_en': main_card.title_en
            }
            target_card.career_path = update_career_path(target_card, new_data)
            
            # 将主记录状态设置为inactive，而不是删除
            main_card.status = 'inactive'
            main_card.updated_by = processed_by or 'system'
            
            # 在Neo4j图数据库中更新Talent节点和career_path
            try:
                # 创建Talent节点属性
                talent_properties = {
                    'name_zh': target_card.name_zh,
                    'name_en': target_card.name_en,
                    'mobile': target_card.mobile,
                    'phone': target_card.phone,
                    'email': target_card.email,
                    'status': target_card.status,
                    'birthday': target_card.birthday.strftime('%Y-%m-%d') if target_card.birthday else None,
                    'age': target_card.age,
                    'residence': target_card.residence,
                    'native_place': target_card.native_place,
                    'pg_id': target_card.id,  # PostgreSQL主记录的ID
                    'updated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                }
                
                # 在Neo4j中更新或创建Talent节点
                neo4j_node_id = create_or_get_talent_node(**talent_properties)
                logging.info(f"成功在Neo4j中更新Talent节点，Neo4j ID: {neo4j_node_id}, PostgreSQL ID: {target_card.id}")
                
                # 处理career_path，创建相关的Neo4j节点和关系
                if target_card.career_path and isinstance(target_card.career_path, list):
                    try:
                        # 调用process_career_path函数处理职业轨迹
                        career_result = process_career_path(
                            career_path=target_card.career_path,
                            talent_node_id=neo4j_node_id,
                            talent_name_zh=target_card.name_zh
                        )
                        
                        # 记录处理结果
                        logging.info(f"处理career_path完成，结果: {career_result}")
                                
                    except Exception as career_error:
                        logging.error(f"处理career_path失败: {str(career_error)}")
                        # career_path处理失败不影响主流程，继续执行
                else:
                    logging.info(f"人才记录 {target_card.id} 没有career_path数据，跳过Neo4j关系处理")
                    
            except Exception as neo4j_error:
                logging.error(f"在Neo4j中更新Talent节点失败: {str(neo4j_error)}")
                # Neo4j操作失败不影响主流程，继续执行
            
            result_data = target_card.to_dict()
            
        elif action == 'keep_main':
            # 保留主记录，将status设置为active
            main_card.status = 'active'
            main_card.updated_by = processed_by or 'system'
            
            # 在Neo4j图数据库中更新Talent节点和career_path
            try:
                # 创建Talent节点属性
                talent_properties = {
                    'name_zh': main_card.name_zh,
                    'name_en': main_card.name_en,
                    'mobile': main_card.mobile,
                    'phone': main_card.phone,
                    'email': main_card.email,
                    'status': main_card.status,
                    'birthday': main_card.birthday.strftime('%Y-%m-%d') if main_card.birthday else None,
                    'age': main_card.age,
                    'residence': main_card.residence,
                    'native_place': main_card.native_place,
                    'pg_id': main_card.id,  # PostgreSQL主记录的ID
                    'updated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                }
                
                # 在Neo4j中更新或创建Talent节点
                neo4j_node_id = create_or_get_talent_node(**talent_properties)
                logging.info(f"成功在Neo4j中更新Talent节点，Neo4j ID: {neo4j_node_id}, PostgreSQL ID: {main_card.id}")
                
                # 处理career_path，创建相关的Neo4j节点和关系
                if main_card.career_path and isinstance(main_card.career_path, list):
                    try:
                        # 调用process_career_path函数处理职业轨迹
                        career_result = process_career_path(
                            career_path=main_card.career_path,
                            talent_node_id=neo4j_node_id,
                            talent_name_zh=main_card.name_zh
                        )
                        
                        # 记录处理结果
                        logging.info(f"处理career_path完成，结果: {career_result}")
                                
                    except Exception as career_error:
                        logging.error(f"处理career_path失败: {str(career_error)}")
                        # career_path处理失败不影响主流程，继续执行
                else:
                    logging.info(f"人才记录 {main_card.id} 没有career_path数据，跳过Neo4j关系处理")
                    
            except Exception as neo4j_error:
                logging.error(f"在Neo4j中更新Talent节点失败: {str(neo4j_error)}")
                # Neo4j操作失败不影响主流程，继续执行
            
            result_data = main_card.to_dict()
            
        elif action == 'ignore':
            # 忽略，将主记录status设置为active
            main_card.status = 'active'
            main_card.updated_by = processed_by or 'system'
            
            # 在Neo4j图数据库中更新Talent节点和career_path
            try:
                # 创建Talent节点属性
                talent_properties = {
                    'name_zh': main_card.name_zh,
                    'name_en': main_card.name_en,
                    'mobile': main_card.mobile,
                    'phone': main_card.phone,
                    'email': main_card.email,
                    'status': main_card.status,
                    'birthday': main_card.birthday.strftime('%Y-%m-%d') if main_card.birthday else None,
                    'age': main_card.age,
                    'residence': main_card.residence,
                    'native_place': main_card.native_place,
                    'pg_id': main_card.id,  # PostgreSQL主记录的ID
                    'updated_at': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
                }
                
                # 在Neo4j中更新或创建Talent节点
                neo4j_node_id = create_or_get_talent_node(**talent_properties)
                logging.info(f"成功在Neo4j中更新Talent节点，Neo4j ID: {neo4j_node_id}, PostgreSQL ID: {main_card.id}")
                
                # 处理career_path，创建相关的Neo4j节点和关系
                if main_card.career_path and isinstance(main_card.career_path, list):
                    try:
                        # 调用process_career_path函数处理职业轨迹
                        career_result = process_career_path(
                            career_path=main_card.career_path,
                            talent_node_id=neo4j_node_id,
                            talent_name_zh=main_card.name_zh
                        )
                        
                        # 记录处理结果
                        logging.info(f"处理career_path完成，结果: {career_result}")
                                
                    except Exception as career_error:
                        logging.error(f"处理career_path失败: {str(career_error)}")
                        # career_path处理失败不影响主流程，继续执行
                else:
                    logging.info(f"人才记录 {main_card.id} 没有career_path数据，跳过Neo4j关系处理")
                    
            except Exception as neo4j_error:
                logging.error(f"在Neo4j中更新Talent节点失败: {str(neo4j_error)}")
                # Neo4j操作失败不影响主流程，继续执行
            
            result_data = main_card.to_dict()
        
        # 所有操作都更新duplicate_record的状态为processed
        duplicate_record.processing_status = 'processed'
        duplicate_record.processed_at = datetime.now()
        duplicate_record.processed_by = processed_by or 'system'
        duplicate_record.processing_notes = notes or f'执行操作: {action}'
        
        db.session.commit()
        
        return {
            'code': 200,
            'success': True,
            'message': f'重复记录处理成功，操作: {action}',
            'data': {
                'duplicate_record': duplicate_record.to_dict(),
                'result': result_data
            }
        }
    
    except Exception as e:
        db.session.rollback()
        error_msg = f"处理重复记录失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }


def get_duplicate_record_detail(duplicate_id):
    """
    获取重复记录详情
    
    Args:
        duplicate_id (int): 重复记录ID
        
    Returns:
        dict: 包含重复记录详细信息
    """
    try:
        duplicate_record = DuplicateBusinessCard.query.filter_by(main_card_id=duplicate_id).first()
        if not duplicate_record:
            return {
                'code': 404,
                'success': False,
                'message': f'未找到main_card_id为{duplicate_id}的重复记录',
                'data': None
            }
        
        record_dict = duplicate_record.to_dict()
        
        if duplicate_record.main_card:
            record_dict['main_card'] = duplicate_record.main_card.to_dict()
        else:
            record_dict['main_card'] = None
        
        suspected_duplicates_details = []
        if duplicate_record.suspected_duplicates:
            for suspected_item in duplicate_record.suspected_duplicates:
                try:
                    if isinstance(suspected_item, dict):
                        card_id = suspected_item.get('id')
                    else:
                        card_id = suspected_item
                    
                    if card_id:
                        card_result = get_business_card(card_id)
                        if card_result['success']:
                            suspected_duplicates_details.append(card_result['data'])
                except Exception as e:
                    logging.warning(f"获取疑似重复记录详情失败: {str(e)}")
                    continue
        
        record_dict['suspected_duplicates_details'] = suspected_duplicates_details
        
        return {
            'code': 200,
            'success': True,
            'message': '获取重复记录详情成功',
            'data': record_dict
        }
    
    except Exception as e:
        error_msg = f"获取重复记录详情失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }


def fix_broken_duplicate_records():
    """
    修复损坏的重复记录
    
    Returns:
        dict: 包含修复结果
    """
    try:
        broken_records = DuplicateBusinessCard.query.filter_by(main_card_id=None).all()
        
        fixed_count = 0
        for record in broken_records:
            db.session.delete(record)
            fixed_count += 1
        
        db.session.commit()
        
        return {
            'code': 200,
            'success': True,
            'message': f'成功修复 {fixed_count} 条损坏的重复记录',
            'data': {'fixed_count': fixed_count}
        }
    
    except Exception as e:
        db.session.rollback()
        error_msg = f"修复重复记录失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }



def create_talent_tag(tag_data):
    """
    创建人才标签节点
    
    Args:
        tag_data: 包含标签信息的字典，包括:
            - name_zh: 标签名称
            - category: 标签分类
            - description: 标签描述
            - status: 启用状态
    
    Returns:
        dict: 操作结果字典
    """
    try:
        from app.services.neo4j_driver import neo4j_driver
        
        # 验证必要参数存在
        if not tag_data or 'name_zh' not in tag_data or not tag_data['name_zh']:
            return {
                'code': 400,
                'success': False,
                'message': '标签名称为必填项',
                'data': None
            }
        
        # 准备节点属性
        tag_properties = {
            'name_zh': tag_data.get('name_zh'),
            'category': tag_data.get('category', '未分类'),
            'describe': tag_data.get('description', ''),  # 使用describe与现有系统保持一致
            'status': tag_data.get('status', 'active'),
            'time': datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        }
        
        # 生成标签的英文名（可选）
        from app.core.graph.graph_operations import create_or_get_node
        
        # 如果提供了名称，尝试获取英文翻译
        if 'name_zh' in tag_data and tag_data['name_zh']:
            try:
                from app.api.data_interface.routes import translate_and_parse
                en_name = translate_and_parse(tag_data['name_zh'])
                tag_properties['name_en'] = en_name[0] if en_name and isinstance(en_name, list) else ''
            except Exception as e:
                logging.warning(f"获取标签英文名失败: {str(e)}")
                tag_properties['name_en'] = ''
                
        # 创建节点
        node_id = create_or_get_node('DataLabel', **tag_properties)
        
        if node_id:
            return {
                'code': 200,
                'success': True,
                'message': '人才标签创建成功',
                'data': {
                    'id': node_id,
                    **tag_properties
                }
            }
        else:
            return {
                'code': 500,
                'success': False,
                'message': '人才标签创建失败',
                'data': None
            }
            
    except Exception as e:
        logging.error(f"创建人才标签失败: {str(e)}", exc_info=True)
        return {
            'code': 500,
            'success': False,
            'message': f'创建人才标签失败: {str(e)}',
            'data': None
        }

def get_talent_tag_list():
    """
    从Neo4j图数据库获取人才标签列表
    
    Returns:
        dict: 包含操作结果和标签列表的字典
    """
    try:
        from app.services.neo4j_driver import neo4j_driver
        
        # 构建Cypher查询语句，获取分类为talent的标签
        query = """
        MATCH (n:DataLabel)
        WHERE n.category CONTAINS 'talentmap' OR n.category CONTAINS '人才地图'
        RETURN id(n) as id, n.name_zh as name_zh, n.name_en as name_en, 
               n.category as category, n.describe as describe, 
               n.status as status, n.time as time, n.node_type as node_type
        ORDER BY n.time DESC
        """
        
        # 执行查询
        tags = []
        with neo4j_driver.get_session() as session:
            result = session.run(query)
            
            # 处理查询结果
            for record in result:
                tag = {
                    'id': record['id'],
                    'name_zh': record['name_zh'],
                    'name_en': record['name_en'],
                    'category': record['category'],
                    'describe': record['describe'],
                    'status': record['status'],
                    'time': record['time'],
                    'node_type': record['node_type']
                }
                tags.append(tag)
        
        return {
            'code': 200,
            'success': True,
            'message': '获取人才标签列表成功',
            'data': tags
        }
        
    except Exception as e:
        error_msg = f"获取人才标签列表失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': []
        }

def update_talent_tag(tag_id, tag_data):
    """
    更新人才标签节点属性
    
    Args:
        tag_id: 标签节点ID
        tag_data: 包含更新信息的字典，可能包括:
            - name_zh: 标签名称
            - category: 标签分类
            - description: 标签描述
            - status: 启用状态
    
    Returns:
        dict: 操作结果字典
    """
    try:
        from app.services.neo4j_driver import neo4j_driver
        
        # 准备要更新的属性
        update_properties = {}
        
        # 检查并添加需要更新的属性
        if 'name_zh' in tag_data and tag_data['name_zh']:
            update_properties['name_zh'] = tag_data['name_zh']
            
            # 如果名称更新了，尝试更新英文名称
            try:
                from app.api.data_interface.routes import translate_and_parse
                en_name = translate_and_parse(tag_data['name_zh'])
                update_properties['name_en'] = en_name[0] if en_name and isinstance(en_name, list) else ''
            except Exception as e:
                logging.warning(f"更新标签英文名失败: {str(e)}")
        
        if 'category' in tag_data and tag_data['category']:
            update_properties['category'] = tag_data['category']
            
        if 'description' in tag_data:
            update_properties['describe'] = tag_data['description']
            
        if 'status' in tag_data:
            update_properties['status'] = tag_data['status']
            
        # 添加更新时间
        update_properties['time'] = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        # 如果没有可更新的属性，返回错误
        if not update_properties:
            return {
                'code': 400,
                'success': False,
                'message': '未提供任何可更新的属性',
                'data': None
            }
        
        # 构建更新的Cypher查询
        set_clauses = []
        params = {'nodeId': tag_id}
        
        for key, value in update_properties.items():
            param_name = f"param_{key}"
            set_clauses.append(f"n.{key} = ${param_name}")
            params[param_name] = value
            
        set_clause = ", ".join(set_clauses)
        
        query = f"""
        MATCH (n:DataLabel)
        WHERE id(n) = $nodeId
        SET {set_clause}
        RETURN id(n) as id, n.name_zh as name_zh, n.name_en as name_en, 
               n.category as category, n.describe as description, 
               n.status as status, n.time as time
        """
        
        # 执行更新查询
        with neo4j_driver.get_session() as session:
            result = session.run(query, **params)
            record = result.single()
            
            if not record:
                return {
                    'code': 404,
                    'success': False,
                    'message': f'未找到ID为{tag_id}的标签',
                    'data': None
                }
                
            # 提取更新后的标签信息
            updated_tag = {
                'id': record['id'],
                'name_zh': record['name_zh'],
                'name_en': record['name_en'],
                'category': record['category'],
                'description': record['description'],
                'status': record['status'],
                'time': record['time']
            }
            
            return {
                'code': 200,
                'success': True,
                'message': '人才标签更新成功',
                'data': updated_tag
            }
            
    except Exception as e:
        error_msg = f"更新人才标签失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }

def delete_talent_tag(tag_id):
    """
    删除人才标签节点及其相关关系
    
    Args:
        tag_id: 标签节点ID
    
    Returns:
        dict: 操作结果字典
    """
    try:
        from app.services.neo4j_driver import neo4j_driver
        
        # 首先获取要删除的标签信息，以便在成功后返回
        get_query = """
        MATCH (n:DataLabel)
        WHERE id(n) = $nodeId
        RETURN id(n) as id, n.name_zh as name_zh, n.name_en as name_en, 
               n.category as category, n.describe as description, 
               n.status as status, n.time as time
        """
        
        # 构建删除节点和关系的Cypher查询
        delete_query = """
        MATCH (n:DataLabel)
        WHERE id(n) = $nodeId
        OPTIONAL MATCH (n)-[r]-()
        DELETE r, n
        RETURN count(n) AS deleted
        """
        
        # 执行查询
        tag_info = None
        with neo4j_driver.get_session() as session:
            # 先获取标签信息
            result = session.run(get_query, nodeId=tag_id)
            record = result.single()
            
            if not record:
                return {
                    'code': 404,
                    'success': False,
                    'message': f'未找到ID为{tag_id}的标签',
                    'data': None
                }
                
            # 保存标签信息用于返回
            tag_info = {
                'id': record['id'],
                'name_zh': record['name_zh'],
                'name_en': record['name_en'],
                'category': record['category'],
                'description': record['description'],
                'status': record['status'],
                'time': record['time']
            }
            
            # 执行删除操作
            delete_result = session.run(delete_query, nodeId=tag_id)
            delete_record = delete_result.single()
            deleted = delete_record['deleted'] if delete_record else 0
            
            if deleted > 0:
                return {
                    'code': 200,
                    'success': True,
                    'message': '人才标签删除成功',
                    'data': tag_info
                }
            else:
                return {
                    'code': 404,
                    'success': False,
                    'message': f'未能删除ID为{tag_id}的标签',
                    'data': None
                }
            
    except Exception as e:
        error_msg = f"删除人才标签失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }

def query_neo4j_graph(query_requirement):
    """
    查询Neo4j图数据库，通过阿里千问API生成Cypher脚本
    
    优化特性：
    - 当有标签名称时，使用递归遍历逻辑
    - 以标签名称为起点，查找WORK_AS、BELONGS_TO、WORK_FOR关系
    - 新的节点按照同样的查找逻辑继续找，一直找到没有指向关系的节点或者Talent节点则停止遍历
    - 检索结果去重后形成最终结果
    - 使用可变长度路径匹配（*1..10），最大遍历深度为10层，避免无限循环
    
    Args:
        query_requirement (str): 查询需求描述
        
    Returns:
        dict: 包含查询结果的字典，JSON格式
    """
    try:
        # 导入必要的模块
        from app.services.neo4j_driver import neo4j_driver
        from openai import OpenAI
        import json
        
        # 阿里千问API配置
        api_key = QWEN_TEXT_API_KEY
        base_url = QWEN_TEXT_BASE_URL
        model_name = QWEN_TEXT_MODEL
        
        # 初始化OpenAI客户端（配置为阿里云API）
        client = OpenAI(
            api_key=api_key,
            base_url=base_url,
        )
        
        # 步骤1: 从Neo4j获取所有标签列表
        logging.info("第一步：从Neo4j获取人才类别的标签列表")
        all_labels_query = """
        MATCH (dl:DataLabel)
        WHERE dl.category CONTAINS '人才地图' OR dl.category CONTAINS 'talentmap'
        RETURN dl.name_zh as name_zh, dl.name_en as name_en
        """
        
        all_labels = []
        with neo4j_driver.get_session() as session:
            result = session.run(all_labels_query)
            for record in result:
                all_labels.append(record['name_zh'])
        
        logging.info(f"获取到{len(all_labels)}个人才标签: {all_labels}")
        
        # 步骤2: 使用阿里千问判断查询需求中的关键信息与标签的对应关系
        logging.info("第二步：调用阿里千问API匹配查询需求与标签")
        
        # 构建所有标签的JSON字符串
        labels_json = json.dumps(all_labels, ensure_ascii=False)
        
        # 构建匹配标签的提示语
        matching_prompt = f"""
        请从上传的查询需求文本中提取以下结构化信息。其中datalabel字段从可用标签列表里进行匹配，匹配结果填写可用标签列表里的标签名称。hotel字段提取查询需求中提到的酒店名称。需要严格按照JSON格式输出：   
        {{
         "basic_info": {{
            "中文姓名": "",
            "英文姓名": "",
            "手机号": "",
            "固定电话": "",
            "电子邮箱": "",
            "生日": "",
            "年龄": "",
            "性别": "",
            "居住地": "",
            "籍贯": ""
        }},
         "datalabel": [
            "标签1","标签2","标签3"
        ],
         "hotel": [
            "酒店名称1","酒店名称2","酒店名称3"
        ]
        }}
        ## 查询需求文本
        {query_requirement}
        
        ## 可用标签列表
        {labels_json}
        
        输出要求：
        1. 中文名称优先，有英文名称也要提取保留
        2. 年龄字段只需填写数字
        3. 标签没有被匹配到，datalabel字段可以为空数组
        4. 酒店名称提取查询需求中明确提到的酒店名称
        5. 如果没有提到酒店信息，hotel字段可以为空数组
        6. datalabel只能填写可用标签列表中的名称，不能填写查询需求文本里的名称
        7. 只需返回JSON字符串，不要返回其他信息
        """
        
        # 调用阿里千问API匹配标签
        logging.info("发送请求到阿里千问API匹配标签："+matching_prompt)
        
        completion = client.chat.completions.create(
            model="qwen-long-latest",  # 使用qwen-long-latest模型
            messages=[
                {"role": "system", "content": "你是一个专业的文本信息提取专家。"},
                {"role": "user", "content": matching_prompt}
            ],
            temperature=0.1,
            response_format={"type": "json_object"}
        )
        
        # 解析API响应
        matching_content = completion.choices[0].message.content
        
        # 直接解析JSON响应，提取datalabel和hotel字段
        if not matching_content:
            raise Exception("API返回内容为空")
        parsed_content = json.loads(matching_content)
        matched_labels = parsed_content.get('datalabel', [])
        matched_hotels = parsed_content.get('hotel', [])
        
        logging.info(f"匹配到的标签: {matched_labels}")
        logging.info(f"匹配到的酒店: {matched_hotels}")
        
        # 步骤3: 构建查询逻辑和Cypher语句
        logging.info("第三步：构建查询逻辑和Cypher语句")
        
        # 提取basic_info中的非空字段
        basic_info = parsed_content.get('basic_info', {})
        non_empty_fields = {k: v for k, v in basic_info.items() if v and str(v).strip()}
        
        logging.info(f"提取到的非空字段: {non_empty_fields}")
        
        # 构建Talent节点子集查询
        talent_conditions = []
        talent_params = {}
        
        if non_empty_fields:
            # 如果有非空字段，构建Talent节点属性匹配条件
            for field, value in non_empty_fields.items():
                if field == "中文姓名":
                    talent_conditions.append("t.name_zh CONTAINS $name_zh")
                    talent_params['name_zh'] = value
                elif field == "英文姓名":
                    talent_conditions.append("t.name_en CONTAINS $name_en")
                    talent_params['name_en'] = value
                elif field == "手机号":
                    talent_conditions.append("t.mobile CONTAINS $mobile")
                    talent_params['mobile'] = value
                elif field == "固定电话":
                    talent_conditions.append("t.phone CONTAINS $phone")
                    talent_params['phone'] = value
                elif field == "电子邮箱":
                    talent_conditions.append("t.email CONTAINS $email")
                    talent_params['email'] = value
                elif field == "生日":
                    # 格式化生日为YYYY-MM-DD格式
                    try:
                        from datetime import datetime
                        # 尝试解析各种可能的日期格式
                        if isinstance(value, str):
                            # 处理常见的日期格式
                            if len(value) == 8 and value.isdigit():  # YYYYMMDD
                                formatted_birthday = f"{value[:4]}-{value[4:6]}-{value[6:8]}"
                            elif len(value) == 10 and value.count('-') == 2:  # YYYY-MM-DD
                                formatted_birthday = value
                            elif len(value) == 10 and value.count('/') == 2:  # YYYY/MM/DD
                                date_obj = datetime.strptime(value, '%Y/%m/%d')
                                formatted_birthday = date_obj.strftime('%Y-%m-%d')
                            else:
                                # 尝试其他常见格式
                                for fmt in ['%Y-%m-%d', '%Y/%m/%d', '%Y.%m.%d', '%Y年%m月%d日']:
                                    try:
                                        date_obj = datetime.strptime(value, fmt)
                                        formatted_birthday = date_obj.strftime('%Y-%m-%d')
                                        break
                                    except ValueError:
                                        continue
                                else:
                                    # 如果所有格式都失败，使用原始值
                                    formatted_birthday = value
                        else:
                            formatted_birthday = str(value)
                        
                        talent_conditions.append("t.birthday = $birthday")
                        talent_params['birthday'] = formatted_birthday
                        logging.info(f"生日字段格式化: {value} -> {formatted_birthday}")
                    except Exception as e:
                        logging.warning(f"生日字段格式化失败: {value}, 错误: {str(e)}")
                        # 如果格式化失败，使用原始值
                        talent_conditions.append("t.birthday = $birthday")
                        talent_params['birthday'] = value
                elif field == "年龄":
                    talent_conditions.append("t.age = $age")
                    talent_params['age'] = int(value) if value.isdigit() else ''
                elif field == "居住地":
                    talent_conditions.append("t.residence CONTAINS $residence")
                    talent_params['residence'] = value
                elif field == "籍贯":
                    talent_conditions.append("t.origin CONTAINS $origin")
                    talent_params['origin'] = value
                elif field == "性别":
                    talent_conditions.append("t.gender = $gender")
                    talent_params['gender'] = value
        
        # 构建Talent子集查询
        if talent_conditions:
            talent_subset_query = f"""
            MATCH (t:Talent)
            WHERE {' AND '.join(talent_conditions)}
            WITH t
            """
            logging.info("构建Talent子集查询条件")
        else:
            talent_subset_query = """
            MATCH (t:Talent)
            WITH t
            """
            logging.info("使用所有Talent节点")
        
        # 构建条件子集查询（DataLabel节点和Hotel节点）
        condition_params = {}
        
        if matched_labels:
            condition_params['labels'] = matched_labels
            logging.info(f"构建DataLabel条件查询，标签: {matched_labels}")
        
        if matched_hotels:
            condition_params['hotels'] = matched_hotels
            logging.info(f"构建Hotel条件查询，酒店: {matched_hotels}")
        
        # 确保参数不为空时才添加到查询中
        if not matched_labels:
            # 如果没有标签，需要修改Cypher查询以避免引用$labels参数
            logging.info("没有标签条件，将调整Cypher查询")
        
        # 步骤4: 执行查询并返回结果
        logging.info("第四步：执行查询并返回结果")
        
        # 检查是否有查询条件，如果都没有则直接返回空结果
        if not talent_conditions and not matched_labels and not matched_hotels:
            logging.info("没有查询条件，直接返回空结果")
            return {
                'code': 200,
                'success': True,
                'message': '查询条件没有匹配到任何人才，返回空结果',
                'query': '查询条件没有匹配到任何人才，返回空结果',
                'matched_labels': matched_labels,
                'matched_hotels': matched_hotels,
                'non_empty_fields': non_empty_fields,
                'data': []
            }
        
        # 构建完整的Cypher查询语句
        # 优化说明：当有标签名称时，使用递归遍历逻辑
        # 以标签名称为起点，查找WORK_AS、BELONGS_TO、WORK_FOR关系
        # 新的节点按照同样的查找逻辑继续找，一直找到没有指向关系的节点或者Talent节点则停止遍历
        # 检索结果去重后形成最终结果
        
        if matched_hotels and matched_labels:
            # 情况1：提供了酒店名称和标签名称
            # 通过酒店名称查到一组Talent节点，通过标签查到另一组Talent节点，两组节点组合去重
            logging.info("情况1：同时有酒店名称和标签名称，使用组合查询方式")
            
            cypher_script = f"""
            // 查询通过酒店名称匹配的Talent节点
            {talent_subset_query}
            WHERE EXISTS {{
              // 条件：存在WORK_FOR关系链路，且酒店名称匹配
              MATCH (t)-[:WORK_FOR]->(h:Hotel)
              WHERE h.hotel_zh IN $hotels
            }}
            RETURN DISTINCT
              t.pg_id AS pg_id,
              t.name_zh AS name_zh,
              t.name_en AS name_en,
              t.gender AS gender,
              t.mobile AS mobile,
              t.email AS email,
              t.updated_at AS updated_at
            
            UNION
            
            // 查询通过标签递归遍历匹配的Talent节点
            // 使用递归遍历：以标签为起点，查找WORK_AS、BELONGS_TO、WORK_FOR关系，递归遍历直到找到Talent节点
            WITH $labels AS targetLabels
            
            // 递归遍历：从标签节点开始，通过关系网络找到所有相关的Talent节点
            // 使用可变长度路径匹配，最大遍历深度：10层，避免无限循环
            MATCH path = (startTag:DataLabel)-[:BELONGS_TO|WORK_AS|WORK_FOR*1..10]-(t:Talent)
            WHERE startTag.name_zh IN targetLabels
            {f"AND {' AND '.join(talent_conditions)}" if talent_conditions else ""}
            
            // 返回去重结果
            RETURN DISTINCT
              t.pg_id AS pg_id,
              t.name_zh AS name_zh,
              t.name_en AS name_en,
              t.gender AS gender,
              t.mobile AS mobile,
              t.email AS email,
              t.updated_at AS updated_at
            """
            
        elif matched_hotels and not matched_labels:
            # 情况2：只提供了酒店名称，没有标签名称
            # 查询Talent的WORK_FOR与指定酒店的关系
            cypher_script = f"""
            {talent_subset_query}
            WHERE EXISTS {{
              // 条件：存在WORK_FOR关系链路，且酒店名称匹配
              MATCH (t)-[:WORK_FOR]->(h:Hotel)
              WHERE h.hotel_zh IN $hotels
            }}
            RETURN DISTINCT 
              t.pg_id AS pg_id, 
              t.name_zh AS name_zh, 
              t.name_en AS name_en,
              t.gender AS gender,
              t.mobile AS mobile, 
              t.email AS email, 
              t.updated_at AS updated_at
            """
            
        elif not matched_hotels and matched_labels:
            # 情况3：没有提供酒店名称，但是有指定的标签名称
            # 通过标签递归遍历查询Talent节点
            logging.info("情况3：只有标签名称，使用标签递归遍历查询方式")
            cypher_script = f"""
            // 递归遍历：以标签为起点，查找WORK_AS、BELONGS_TO、WORK_FOR关系，递归遍历直到找到Talent节点
            
            // 步骤1: 定义标签条件列表
            WITH $labels AS targetLabels
            
            // 步骤2: 递归遍历关系网络
            // 使用可变长度路径匹配，从标签节点开始，通过关系网络找到所有相关的Talent节点
            // 关系类型：BELONGS_TO、WORK_AS、WORK_FOR
            // 最大遍历深度：10层，避免无限循环
            
            // 方法1: 使用标准Cypher可变长度路径匹配（推荐）
            MATCH path = (startTag:DataLabel)-[:BELONGS_TO|WORK_AS|WORK_FOR*1..10]-(t:Talent)
            WHERE startTag.name_zh IN targetLabels
            {f"AND {' AND '.join(talent_conditions)}" if talent_conditions else ""}
            
            // 步骤3: 返回去重结果
            RETURN DISTINCT
              t.pg_id AS pg_id,
              t.name_zh AS name_zh,
              t.name_en AS name_en,
              t.gender AS gender,
              t.mobile AS mobile,
              t.email AS email,
              t.updated_at AS updated_at
            
            // 注意：如果需要更高级的路径遍历控制，可以使用APOC扩展的apoc.path.expandConfig
            // 但标准Cypher的可变长度路径匹配已经能够满足大部分递归遍历需求
            """
            
        else:
            # 情况4：没有指定标签，也没有指定酒店
            # 只按照Talent属性进行查询
            cypher_script = f"""
            {talent_subset_query}
            RETURN DISTINCT 
              t.pg_id AS pg_id, 
              t.name_zh AS name_zh, 
              t.name_en AS name_en,
              t.gender AS gender,
              t.mobile AS mobile, 
              t.email AS email, 
              t.updated_at AS updated_at
            """
        
        logging.info(f"生成的Cypher脚本: {cypher_script}")
        
        # 合并所有参数
        all_params = {**talent_params, **condition_params}
        
        # 执行查询
        with neo4j_driver.get_session() as session:
            result = session.run(cypher_script, **all_params)
            records = [record.data() for record in result]
            
        # 构建查询结果
        response_data = {
            'code': 200,
            'success': True,
            'message': '查询成功执行',
            'query': cypher_script,
            'matched_labels': matched_labels,
            'matched_hotels': matched_hotels,
            'non_empty_fields': non_empty_fields,
            'data': records
        }
        
        return response_data
        
    except Exception as e:
        error_msg = f"查询Neo4j图数据库失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': []
        }

def talent_get_tags(talent_id):
    """
    根据talent ID获取人才节点关联的标签
    
    Args:
        talent_id (int): 人才节点pg_id
        
    Returns:
        dict: 包含人才ID和关联标签的字典，JSON格式
    """
    try:
        # 导入必要的模块
        from app.services.neo4j_driver import neo4j_driver
        
        # 准备查询返回数据
        response_data = {
            'code': 200,
            'success': True,
            'message': '获取人才标签成功',
            'data': []
        }
        
        # 构建Cypher查询语句，获取人才节点关联的标签
        cypher_query = """
        MATCH (t:Talent)-[r:BELONGS_TO|WORK_AS]->(tag:DataLabel)
        WHERE t.pg_id = $talent_id
        RETURN t.pg_id as talent_pg_id, tag.name_zh as name_zh, type(r) as relation_type
        """
        
        # 执行查询
        with neo4j_driver.get_session() as session:
            result = session.run(cypher_query, talent_id=int(talent_id))
            records = list(result)
            
            # 如果没有查询到标签，返回空数组
            if not records:
                response_data['message'] = f'人才pg_id {talent_id} 没有关联的标签'
                return response_data
            
            # 处理查询结果
            for record in records:
                talent_tag = {
                    'talent_pg_id': record['talent_pg_id'],
                    'name_zh': record['name_zh'],
                    'relation_type': record['relation_type']
                }
                response_data['data'].append(talent_tag)
            
        return response_data
    
    except Exception as e:
        error_msg = f"获取人才标签失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': []
        }

def talent_update_tags(data):
    """
    根据传入的JSON数据为人才节点创建与标签的BELONGS_TO关系
    
    Args:
        data (list): 包含talent和tag字段的对象列表
            例如: [
                {"talent": 12345, "tag": "市场营销"},
                {"talent": 12345, "tag": "酒店管理"}
            ]
        
    Returns:
        dict: 操作结果和状态信息
    """
    try:
        # 导入必要的模块
        from app.services.neo4j_driver import neo4j_driver
        
        # 验证输入参数
        if not isinstance(data, list):
            return {
                'code': 400,
                'success': False,
                'message': '参数格式错误，需要JSON数组',
                'data': None
            }
        
        if len(data) == 0:
            return {
                'code': 400,
                'success': False,
                'message': '数据列表为空',
                'data': None
            }
        
        # 获取当前时间
        current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
        
        # 成功和失败计数
        success_count = 0
        failed_items = []
        
        # 按talent分组处理数据
        talent_tags = {}
        for item in data:
            # 验证每个项目的格式
            if not isinstance(item, dict) or 'talent' not in item or 'tag' not in item:
                failed_items.append(item)
                continue
                
            talent_id = item.get('talent')
            tag_name = item.get('tag')
            
            # 验证talent_id和tag_name的值
            if not talent_id or not tag_name or not isinstance(tag_name, str):
                failed_items.append(item)
                continue
                
            # 按talent_id分组
            if talent_id not in talent_tags:
                talent_tags[talent_id] = []
                
            talent_tags[talent_id].append(tag_name)
        
        with neo4j_driver.get_session() as session:
            # 处理每个talent及其标签
            for talent_id, tags in talent_tags.items():
                # 首先验证talent节点是否存在
                check_talent_query = """
                MATCH (t:Talent) 
                WHERE t.pg_id = $talent_id
                RETURN t
                """
                talent_result = session.run(check_talent_query, talent_id=int(talent_id))
                if not talent_result.single():
                    # 该talent不存在，记录失败项并继续下一个talent
                    for tag in tags:
                        failed_items.append({'talent_pg_id': talent_id, 'tag': tag})
                    continue
                
                # 首先清除所有现有的BELONGS_TO关系
                clear_relations_query = """
                MATCH (t:Talent)-[r:BELONGS_TO]->(:DataLabel)
                WHERE t.pg_id = $talent_id
                DELETE r
                RETURN count(r) as deleted_count
                """
                clear_result = session.run(clear_relations_query, talent_id=int(talent_id))
                clear_record = clear_result.single()
                deleted_count = clear_record['deleted_count'] if clear_record else 0
                logging.info(f"已删除talent_id={talent_id}的{deleted_count}个已有标签关系")
                
                # 处理每个标签
                for tag_name in tags:
                    try:
                        # 1. 查找或创建标签节点
                        # 先查找是否存在该标签
                        find_tag_query = """
                        MATCH (tag:DataLabel)
                        WHERE tag.name_zh = $tag_name
                        RETURN id(tag) as tag_id
                        """
                        tag_result = session.run(find_tag_query, tag_name=tag_name)
                        tag_record = tag_result.single()
                        
                        if tag_record:
                            tag_id = tag_record['tag_id']
                        else:
                            # 创建新标签
                            create_tag_query = """
                            CREATE (tag:DataLabel {name_zh: $name, category: $category, updated_at: $updated_at})
                            RETURN id(tag) as tag_id
                            """
                            tag_result = session.run(
                                create_tag_query, 
                                name=tag_name,
                                category='talent',
                                updated_at=current_time
                            )
                            tag_record = tag_result.single()
                            if not tag_record:
                                raise Exception(f"创建标签失败: {tag_name}")
                            tag_id = tag_record['tag_id']
                        
                        # 2. 创建人才与标签的BELONGS_TO关系
                        create_relation_query = """
                        MATCH (t:Talent), (tag:DataLabel)
                        WHERE t.pg_id = $talent_id AND tag.name_zh = $tag_name
                        CREATE (t)-[r:BELONGS_TO]->(tag)
                        SET r.created_at = $current_time
                        RETURN r
                        """
                        
                        relation_result = session.run(
                            create_relation_query,
                            talent_id=int(talent_id),
                            tag_name=tag_name,
                            current_time=current_time
                        )
                        
                        if relation_result.single():
                            success_count += 1
                        else:
                            failed_items.append({'talent_pg_id': talent_id, 'tag': tag_name})
                            
                    except Exception as tag_error:
                        logging.error(f"为标签 {tag_name} 创建关系时出错: {str(tag_error)}")
                        failed_items.append({'talent_pg_id': talent_id, 'tag': tag_name})
        
        # 返回结果
        total_items = len(data)
        if success_count == total_items:
            return {
                'code': 200,
                'success': True,
                'message': f'成功创建或更新了 {success_count} 个标签关系',
                'data': {
                    'success_count': success_count,
                    'total_count': total_items,
                    'failed_items': []
                }
            }
        elif success_count > 0:
            return {
                'code': 206, # Partial Content
                'success': True,
                'message': f'部分成功: 创建或更新了 {success_count}/{total_items} 个标签关系',
                'data': {
                    'success_count': success_count,
                    'total_count': total_items,
                    'failed_items': failed_items
                }
            }
        else:
            return {
                'code': 500,
                'success': False,
                'message': '无法创建任何标签关系',
                'data': {
                    'success_count': 0,
                    'total_count': total_items,
                    'failed_items': failed_items
                }
            }
            
    except Exception as e:
        error_msg = f"更新人才标签关系失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }

def parse_text_with_qwen25VLplus(image_data):
    """
    使用阿里云的 Qwen VL Max 模型解析图像中的名片信息
    
    Args:
        image_data (bytes): 图像的二进制数据
        
    Returns:
        dict: 解析的名片信息
    """
    try:
        # 将图片数据转为 base64 编码
        base64_image = base64.b64encode(image_data).decode('utf-8')
        
        # 初始化 OpenAI 客户端，配置为阿里云 API
        client = OpenAI(
            api_key=QWEN_API_KEY,
            base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
        )
        
        # 构建优化后的提示语
        prompt = """你是企业名片的信息提取专家。请仔细分析提供的图片，精确提取名片信息。

## 提取要求
- 区分中英文内容，分别提取
- 保持提取信息的原始格式（如大小写、标点）
- 对于无法识别或名片中不存在的信息，返回空字符串
- 名片中没有的信息，请不要猜测
## 需提取的字段
1. 中文姓名 (name_zh)
2. 英文姓名 (name_en)
3. 中文职位/头衔 (title_zh)
4. 英文职位/头衔 (title_en)
5. 中文酒店/公司名称 (hotel_zh)
6. 英文酒店/公司名称 (hotel_en)
7. 手机号码 (mobile) - 如有多个手机号码，使用逗号分隔，最多提取3个
8. 固定电话 (phone) - 如有多个，使用逗号分隔
9. 电子邮箱 (email)
10. 中文地址 (address_zh)
11. 英文地址 (address_en)
12. 中文邮政编码 (postal_code_zh)
13. 英文邮政编码 (postal_code_en)
14. 生日 (birthday) - 格式为YYYY-MM-DD，如1990-01-01
15. 年龄 (age) - 数字格式，如30，如果无法识别，返回空字符串
16. 籍贯 (native_place) - 出生地或户籍所在地信息
17. 居住地 (residence) - 个人居住地址信息
18. 品牌组合 (brand_group) - 如有多个品牌，使用逗号分隔
19. 职业轨迹 (career_path) - 如能从名片中推断，以JSON数组格式返回，包含当前日期，公司名称和职位。自动生成当前日期。
20. 隶属关系 (affiliation) - 如能从名片中推断，以JSON数组格式返回，包含公司名称和隶属集团名称
## 输出格式
请以严格的JSON格式返回结果，不要添加任何额外解释文字。JSON格式如下：
```json
{
  "name_zh": "",
  "name_en": "",
  "title_zh": "",
  "title_en": "",
  "hotel_zh": "",
  "hotel_en": "",
  "mobile": "",
  "phone": "",
  "email": "",
  "address_zh": "",
  "address_en": "",
  "postal_code_zh": "",
  "postal_code_en": "",
  "birthday": "",
  "age": "",
  "native_place": "",
  "residence": "",
  "brand_group": "",
  "career_path": [],
  "affiliation": []
}
```"""
        
        # 调用 Qwen VL Max  API（添加重试机制）
        max_retries = 3
        retry_delay = 2  # 秒
        
        for attempt in range(max_retries):
            try:
                logging.info(f"发送请求到 Qwen VL Max 模型 (第 {attempt + 1}/{max_retries} 次尝试)")
                completion = client.chat.completions.create(
                    # model="qwen-vl-plus",
                    model="qwen-vl-max-latest",
                    messages=[
                        {
                            "role": "user",
                            "content": [
                                {"type": "text", "text": prompt},
                                {"type": "image_url", "image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}}
                            ]
                        }
                    ],
                    temperature=0.1,  # 降低温度增加精确性
                    response_format={"type": "json_object"},  # 要求输出JSON格式
                    timeout=30  # 30秒超时
                )
                break  # 如果成功，退出重试循环
            except Exception as api_error:
                logging.warning(f"Qwen API 调用失败 (第 {attempt + 1}/{max_retries} 次): {str(api_error)}")
                if attempt == max_retries - 1:  # 最后一次尝试
                    if "Connection error" in str(api_error) or "timeout" in str(api_error).lower():
                        raise Exception("Connection error.")
                    else:
                        raise api_error
                else:
                    time.sleep(retry_delay)  # 等待后重试
                    retry_delay *= 2  # 指数退避
        
        # 解析响应
        response_content = completion.choices[0].message.content
        logging.info(f"成功从 Qwen 模型获取响应: {response_content}")
        
        # 尝试从响应中提取 JSON
        try:
            if not response_content:
                raise Exception("API返回内容为空")
            extracted_data = json.loads(response_content)
            logging.info("成功解析 Qwen 响应中的 JSON")
        except json.JSONDecodeError:
            logging.warning("无法解析 JSON，尝试从文本中提取信息")
            # 这里可以调用其他的解析函数，但为了简化，先返回错误
            raise Exception("无法解析 Qwen 返回的 JSON 格式")
        
        # 确保所有必要字段存在
        required_fields = [
            'name_zh', 'name_en', 'title_zh', 'title_en', 
            'hotel_zh', 'hotel_en', 'mobile', 'phone', 
            'email', 'address_zh', 'address_en',
            'postal_code_zh', 'postal_code_en', 'birthday', 'age', 'native_place', 'residence',
            'brand_group', 'career_path'
        ]
        
        for field in required_fields:
            if field not in extracted_data:
                if field == 'career_path':
                    extracted_data[field] = []
                elif field == 'age':
                    extracted_data[field] = ''
                else:
                    extracted_data[field] = ""
        
        # 为career_path增加一条记录
        if extracted_data.get('hotel_zh') or extracted_data.get('hotel_en') or extracted_data.get('title_zh') or extracted_data.get('title_en'):
            career_entry = {
                'date': datetime.now().strftime('%Y-%m-%d'),
                'hotel_en': extracted_data.get('hotel_en', ''),
                'hotel_zh': extracted_data.get('hotel_zh', ''),
                'image_path': '',
                'source': 'business_card_creation',
                'title_en': extracted_data.get('title_en', ''),
                'title_zh': extracted_data.get('title_zh', '')
            }
            
            # 直接清空原有的career_path内容，用career_entry写入
            extracted_data['career_path'] = [career_entry]
            logging.info(f"为解析结果设置了career_path记录: {career_entry}")
        
        return extracted_data
        
    except Exception as e:
        error_msg = f"Qwen VL Max 模型解析失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        raise Exception(error_msg)


def record_parsed_talents(result):
    """
    将解析结果写入parsed_talents数据库表
    
    Args:
        result (dict): 解析任务的结果数据，包含解析成功的人才信息
        
    Returns:
        dict: 包含操作结果的字典
    """
    try:
        # 检查结果是否成功
        if not result.get('success'):
            return {
                'code': 400,
                'success': False,
                'message': '解析任务未成功，无法记录人才数据'
            }
        
        # 获取解析数据
        parse_data = result.get('data', {})
        if not parse_data:
            return {
                'code': 400,
                'success': False,
                'message': '解析结果中没有数据'
            }
        
        # 提取任务信息
        task_id = parse_data.get('task_id', '')
        task_type = parse_data.get('task_type', '')
        
        # 处理不同格式的解析结果
        talent_records = []
        
        # 检查是否有results字段（批量处理结果）
        if 'results' in parse_data:
            results = parse_data['results']
            for item in results:
                if isinstance(item, dict) and item.get('success') and item.get('data'):
                    talent_data = item['data']
                    if isinstance(talent_data, dict):
                        talent_records.append(talent_data)
        # 检查是否有data字段且为列表
        elif isinstance(parse_data.get('data'), list):
            talent_records = parse_data['data']
        # 检查是否直接是人才数据字典
        elif isinstance(parse_data, dict) and parse_data.get('name_zh'):
            talent_records = [parse_data]
        
        if not talent_records:
            return {
                'code': 400,
                'success': False,
                'message': '未找到有效的人才数据'
            }
        
        # 批量创建ParsedTalent记录
        created_records = []
        failed_records = []
        
        for talent_data in talent_records:
            try:
                # 提取ParsedTalent模型需要的字段
                parsed_talent = ParsedTalent()
                parsed_talent.name_zh = talent_data.get('name_zh', '')
                parsed_talent.name_en = talent_data.get('name_en', '')
                parsed_talent.title_zh = talent_data.get('title_zh', '')
                parsed_talent.title_en = talent_data.get('title_en', '')
                parsed_talent.mobile = talent_data.get('mobile', '')
                parsed_talent.phone = talent_data.get('phone', '')
                parsed_talent.email = talent_data.get('email', '')
                parsed_talent.hotel_zh = talent_data.get('hotel_zh', '')
                parsed_talent.hotel_en = talent_data.get('hotel_en', '')
                parsed_talent.address_zh = talent_data.get('address_zh', '')
                parsed_talent.address_en = talent_data.get('address_en', '')
                parsed_talent.postal_code_zh = talent_data.get('postal_code_zh', '')
                parsed_talent.postal_code_en = talent_data.get('postal_code_en', '')
                parsed_talent.brand_zh = talent_data.get('brand_zh', '')
                parsed_talent.brand_en = talent_data.get('brand_en', '')
                parsed_talent.affiliation_zh = talent_data.get('affiliation_zh', '')
                parsed_talent.affiliation_en = talent_data.get('affiliation_en', '')
                parsed_talent.image_path = talent_data.get('image_path', '')
                parsed_talent.career_path = talent_data.get('career_path', [])
                parsed_talent.brand_group = talent_data.get('brand_group', '')
                parsed_talent.birthday = talent_data.get('birthday')
                parsed_talent.residence = talent_data.get('residence', '')
                parsed_talent.age = talent_data.get('age')
                parsed_talent.native_place = talent_data.get('native_place', '')
                parsed_talent.gender = talent_data.get('gender', '')  # 新增性别字段
                parsed_talent.origin_source = talent_data.get('origin_source', [])
                parsed_talent.talent_profile = talent_data.get('talent_profile', '')
                parsed_talent.task_id = str(task_id) if task_id else ''
                parsed_talent.task_type = task_type
                parsed_talent.status = '待审核'  # 统一设置为待审核状态
                parsed_talent.created_at = datetime.now()
                parsed_talent.updated_by = 'system'
                
                # 添加到数据库会话
                db.session.add(parsed_talent)
                created_records.append(parsed_talent)
                
            except Exception as record_error:
                logging.error(f"创建人才记录失败: {str(record_error)}")
                failed_records.append({
                    'data': talent_data,
                    'error': str(record_error)
                })
        
        # 提交数据库事务
        if created_records:
            db.session.commit()
            logging.info(f"成功创建 {len(created_records)} 条人才记录")
        
        # 构建返回结果
        result_data = {
            'created_count': len(created_records),
            'failed_count': len(failed_records),
            'created_records': [record.to_dict() for record in created_records],
            'failed_records': failed_records
        }
        
        if failed_records:
            return {
                'code': 206,  # 部分成功
                'success': True,
                'message': f'成功创建 {len(created_records)} 条记录，失败 {len(failed_records)} 条'
            }
        else:
            return {
                'code': 200,
                'success': True,
                'message': f'成功创建 {len(created_records)} 条人才记录'
            }
            
    except Exception as e:
        db.session.rollback()
        error_msg = f"记录解析人才数据失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg
        }


def get_parsed_talents(status=None):
    """
    获取解析人才记录列表
    
    Args:
        status (str, optional): 状态过滤参数，如果为空则查询所有记录
        
    Returns:
        dict: 包含操作结果和人才记录列表的字典
    """
    try:
        # 构建查询
        query = ParsedTalent.query
        
        # 如果提供了status参数，则添加状态过滤条件
        if status and status.strip():
            query = query.filter_by(status=status.strip())
        
        # 按创建时间倒序排列
        parsed_talents = query.order_by(ParsedTalent.created_at.desc()).all()
        
        # 转换为字典格式
        talents_data = [talent.to_dict() for talent in parsed_talents]
        
        return {
            'code': 200,
            'success': True,
            'message': f'成功获取 {len(talents_data)} 条解析人才记录',
            'data': talents_data,
            'count': len(talents_data)
        }
    
    except Exception as e:
        error_msg = f"获取解析人才记录失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': [],
            'count': 0
        } 


def create_origin_source_entry(task_type, minio_path):
    """
    创建origin_source字段的单个记录
    
    Args:
        task_type (str): 任务类型
        minio_path (str): MinIO路径
        
    Returns:
        dict: 包含task_type、minio_path和source_date的记录
    """
    return {
        'task_type': task_type,
        'minio_path': minio_path,
        'source_date': datetime.now().strftime('%Y-%m-%d')
    }


def update_origin_source(existing_origin_source, task_type, minio_path):
    """
    更新origin_source字段，将新的记录添加到JSON数组中
    
    Args:
        existing_origin_source: 现有的origin_source内容
        task_type (str): 任务类型
        minio_path (str): MinIO路径
        
    Returns:
        list: 更新后的origin_source JSON数组，格式为：
        [
            {"task_type": "名片", "minio_path": "path1", "source_date": "2025-01-01"},
            {"task_type": "简历", "minio_path": "path2", "source_date": "2025-01-02"}
        ]
    """
    try:
        # 解析现有的origin_source
        origin_list = []
        if existing_origin_source:
            if isinstance(existing_origin_source, str):
                try:
                    parsed = json.loads(existing_origin_source)
                    if isinstance(parsed, list):
                        origin_list = parsed
                    elif isinstance(parsed, dict):
                        origin_list = [parsed]
                    else:
                        origin_list = []
                except (json.JSONDecodeError, TypeError):
                    origin_list = []
            elif isinstance(existing_origin_source, list):
                origin_list = existing_origin_source
            elif isinstance(existing_origin_source, dict):
                origin_list = [existing_origin_source]
            else:
                origin_list = []
        
        # 确保origin_list是列表
        if not isinstance(origin_list, list):
            origin_list = []
        
        # 验证现有记录，确保每个元素都符合格式要求
        validated_list = []
        for item in origin_list:
            if isinstance(item, dict) and 'task_type' in item and 'minio_path' in item:
                # 确保有source_date字段，如果没有则添加当前日期
                if 'source_date' not in item:
                    item['source_date'] = datetime.now().strftime('%Y-%m-%d')
                validated_list.append(item)
        
        # 创建新的记录
        new_entry = create_origin_source_entry(task_type, minio_path)
        
        # 检查是否已存在相同的minio_path记录
        existing_paths = [entry.get('minio_path') for entry in validated_list if isinstance(entry, dict)]
        if minio_path not in existing_paths:
            validated_list.append(new_entry)
        
        return validated_list
        
    except Exception as e:
        logging.error(f"更新origin_source失败: {str(e)}")
        # 如果处理失败，返回包含新记录的数组
        return [create_origin_source_entry(task_type, minio_path)]


def get_brand_group_by_hotel(hotel_zh):
    """
    根据酒店中文名称获取对应的品牌和集团信息
    
    Args:
        hotel_zh (str): 酒店中文名称
        
    Returns:
        dict: 包含操作结果和品牌集团信息的字典
    """
    try:
        # 步骤1: 从输入参数获得酒店名称hotel_zh
        if not hotel_zh or not hotel_zh.strip():
            return {
                'code': 400,
                'success': False,
                'message': '酒店名称不能为空',
                'data': None
            }
        
        hotel_name = hotel_zh.strip()
        logging.info(f"开始查询酒店 '{hotel_name}' 的品牌和集团信息")
        
        # 步骤2: 从hotel_group_brands数据库表获取所有品牌名称作为参照值
        try:
            # 使用现有的数据库模型
            from app.core.data_parse.hotel_management import HotelGroupBrands
            
            all_brands = []
            # 查询所有有效的品牌名称
            brands = HotelGroupBrands.query.filter(
                HotelGroupBrands.brand_name_zh.isnot(None),
                HotelGroupBrands.brand_name_zh != '',
                HotelGroupBrands.status == 'active'
            ).distinct(HotelGroupBrands.brand_name_zh).order_by(HotelGroupBrands.brand_name_zh).all()
            
            for brand in brands:
                if brand.brand_name_zh:
                    all_brands.append(brand.brand_name_zh)
            
            if not all_brands:
                return {
                    'code': 404,
                    'success': False,
                    'message': '未找到任何品牌信息',
                    'data': None
                }
            
            logging.info(f"获取到 {len(all_brands)} 个品牌作为参照值")
            
        except Exception as db_error:
            logging.error(f"从数据库获取品牌列表失败: {str(db_error)}")
            return {
                'code': 500,
                'success': False,
                'message': f'获取品牌列表失败: {str(db_error)}',
                'data': None
            }
        
        # 步骤3: 通过阿里千问qwen long模型判断酒店对应的品牌
        try:
            # 构建所有品牌的JSON字符串
            brands_json = json.dumps(all_brands, ensure_ascii=False)
            
            # 构建提示词
            prompt = f"""
            请根据提供的酒店名称，从可用品牌列表中选择最匹配的品牌。
            
            ## 酒店名称
            {hotel_name}
            
            ## 可用品牌列表
            {brands_json}
            
            ## 匹配及输出要求
            1. 仔细分析酒店名称，选择最匹配的一个品牌，不要返回多个品牌
            2. 如果酒店名称中包含品牌信息，优先选择该品牌
            3. 如果酒店名称里有品牌信息，但是品牌信息不在可用品牌列表中，则返回空字符串
            4. 如果相似度很低，则可以返回空字符串
            5. 严格按照JSON格式输出：{{"brand": "品牌名称"}}
            6. 只返回JSON字符串，不要包含其他解释文字。
            """
            
            logging.info(f"开始调用千问API: {prompt}")
            # 调用阿里千问API
            client = OpenAI(
                api_key=QWEN_TEXT_API_KEY,
                base_url=QWEN_TEXT_BASE_URL,
            )
            
            completion = client.chat.completions.create(
                model="qwen-plus-latest",
                messages=[
                    {"role": "system", "content": "你是一个专业的酒店品牌识别专家。"},
                    {"role": "user", "content": prompt}
                ],
                temperature=0.1,
                response_format={"type": "json_object"}
            )
            
            # 解析API响应
            response_content = completion.choices[0].message.content
            logging.info(f"千问API返回结果: {response_content}")
            
            # 解析JSON响应
            try:
                if not response_content:
                    raise Exception("API返回内容为空")
                parsed_response = json.loads(response_content)
                brand_name = parsed_response.get('brand', '')
                
                if not brand_name:
                    return {
                        'code': 404,
                        'success': False,
                        'message': '未能识别出酒店对应的品牌',
                        'data': None
                    }
                
                logging.info(f"识别出的品牌: {brand_name}")
                
            except json.JSONDecodeError as json_error:
                logging.error(f"解析千问API返回的JSON失败: {str(json_error)}")
                return {
                    'code': 500,
                    'success': False,
                    'message': f'解析品牌识别结果失败: {str(json_error)}',
                    'data': None
                }
                
        except Exception as api_error:
            logging.error(f"调用千问API失败: {str(api_error)}")
            return {
                'code': 500,
                'success': False,
                'message': f'品牌识别失败: {str(api_error)}',
                'data': None
            }
        
        # 步骤4: 从hotel_group_brands中查找完整的品牌和集团信息
        try:
            # 使用现有的数据库模型查询品牌信息
            brand_info = None
            brand_record = HotelGroupBrands.query.filter(
                HotelGroupBrands.brand_name_zh == brand_name,
                HotelGroupBrands.status == 'active'
            ).first()
            
            if brand_record:
                brand_info = {
                    'brand_name_zh': brand_record.brand_name_zh,
                    'brand_name_en': brand_record.brand_name_en,
                    'group_name_zh': brand_record.group_name_zh,
                    'group_name_en': brand_record.group_name_en
                }
                logging.info(f"找到品牌信息: {brand_info}")
            else:
                logging.warning(f"未找到品牌 '{brand_name}' 的详细信息")
                return {
                    'code': 404,
                    'success': False,
                    'message': f'未找到品牌 "{brand_name}" 的详细信息',
                    'data': None
                }
            
        except Exception as query_error:
            logging.error(f"查询品牌详细信息失败: {str(query_error)}")
            return {
                'code': 500,
                'success': False,
                'message': f'查询品牌详细信息失败: {str(query_error)}',
                'data': None
            }
        
        # 返回成功结果
        return {
            'code': 200,
            'success': True,
            'message': f'成功获取酒店 "{hotel_name}" 的品牌和集团信息',
            'data': {
                'hotel_zh': hotel_name,
                'brand_name_zh': brand_info['brand_name_zh'],
                'brand_name_en': brand_info['brand_name_en'],
                'group_name_zh': brand_info['group_name_zh'],
                'group_name_en': brand_info['group_name_en']
            }
        }
        
    except Exception as e:
        error_msg = f"获取酒店品牌和集团信息失败: {str(e)}"
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }


def get_brand_by_deepseek(hotel_zh):
    """
    通过DeepSeek API获取酒店的品牌和集团信息
    
    Args:
        hotel_zh (str): 酒店中文名称
    
    Returns:
        dict: 包含操作结果和品牌集团信息的字典
    """
    try:
        if not hotel_zh or not hotel_zh.strip():
            return {
                'code': 400,
                'success': False,
                'message': '酒店名称不能为空',
                'data': None
            }
        
        # 导入OpenAI客户端
        from openai import OpenAI
        
        # 配置DeepSeek API
        client = OpenAI(
            api_key="sk-54fe24fcf5cc49a39c1c68d137010f0c",
            base_url="https://api.deepseek.com/v1"
        )
        
        # 构建提示词
        prompt = f"""
请根据酒店名称"{hotel_zh}"，返回该酒店对应的品牌和集团信息。

请按照以下JSON格式返回结果：
{{
    "brand_name_zh": "品牌中文名称",
    "brand_name_en": "品牌英文名称",
    "group_name_zh": "集团中文名称",
    "group_name_en": "集团英文名称"
}}

要求：
1. 只返回JSON格式的数据，不要包含其他文字
2. 如果无法确定品牌或集团信息，请返回空字符串，不要返回None
3. 确保返回的是有效的JSON格式
4. 请在推理后给出明确的JSON答案
"""
        
        # 调用DeepSeek API
        response = client.chat.completions.create(
            model="deepseek-reasoner",
            messages=[
                {
                    "role": "user",
                    "content": prompt
                }
            ],
            temperature=0.1,
            max_tokens=500
        )
        
        # 获取响应内容
        response_content = response.choices[0].message.content
        if not response_content:
            raise Exception("DeepSeek API返回内容为空")
        
        response_content = response_content.strip()
        
        # 解析JSON响应
        import json
        brand_info = json.loads(response_content)
        
        # 验证返回的数据结构
        if not isinstance(brand_info, dict):
            raise Exception("DeepSeek API返回的数据格式不正确")
        
        brand_name_zh = brand_info.get('brand_name_zh', '')
        brand_name_en = brand_info.get('brand_name_en', '')
        group_name_zh = brand_info.get('group_name_zh', '')
        group_name_en = brand_info.get('group_name_en', '')
        
        logging.info(f"DeepSeek成功获取酒店品牌信息: {hotel_zh} -> 品牌: {brand_name_zh}, 集团: {group_name_zh}")
        
        return {
            'code': 200,
            'success': True,
            'message': f'成功获取酒店 "{hotel_zh}" 的品牌和集团信息',
            'data': {
                'hotel_zh': hotel_zh,
                'brand_name_zh': brand_name_zh,
                'brand_name_en': brand_name_en,
                'group_name_zh': group_name_zh,
                'group_name_en': group_name_en
            }
        }
        
    except Exception as json_error:
        if 'json' in str(type(json_error)):
            error_msg = f"解析DeepSeek API返回的JSON数据失败: {str(json_error)}"
        else:
            error_msg = f"通过DeepSeek获取酒店品牌和集团信息失败: {str(json_error)}"
        
        logging.error(error_msg, exc_info=True)
        
        return {
            'code': 500,
            'success': False,
            'message': error_msg,
            'data': None
        }