# ocr - OCR识别路由
# Version: 0.0.1
# 更新：

import os
# 更新：
import uuid
# 更新：
import base64
# 更新：
import httpx
# 更新：
from fastapi import APIRouter, Depends, HTTPException, UploadFile, File
# 更新：
from sqlalchemy.orm import Session
# 更新：
from app.core.database import get_db
# 更新：
from app.core.auth import get_current_user
# 更新：
from app.models.models import User
# 更新：

# 更新：
router = APIRouter(prefix="/api/ocr", tags=["OCR 识别"])
# 更新：

# 更新：
# 阿里云 DashScope API 配置
# 更新：
DASHSCOPE_API_KEY = os.getenv("DASHSCOPE_API_KEY", "sk-9389024a37da4f7bb455ac9a6b28776f")
# 更新：

# 更新：
# 阿里云 OSS 配置
# 更新：
OSS_CONFIG = {
# 更新：
    "access_key_id": os.getenv("OSS_ACCESS_KEY_ID", "LTAI5t6HUnpFBLEK9194kPVG"),
# 更新：
    "access_key_secret": os.getenv("OSS_ACCESS_KEY_SECRET", "LEr4Q8yRxb8D5b24cKfCwlt4MMoke1"),
# 更新：
    "bucket_name": "jiachenlong-oss",
# 更新：
    "endpoint": "oss-cn-hangzhou.aliyuncs.com",
# 更新：
    "public_url": "https://jiachenlong-oss.oss-cn-hangzhou.aliyuncs.com"
# 更新：
}
# 更新：

# 更新：
# 临时上传目录（用于OCR识别本地备选）
# 更新：
UPLOAD_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "uploads", "temp")
# 更新：
os.makedirs(UPLOAD_DIR, exist_ok=True)
# 更新：

# 更新：

# 更新：
def get_oss_path(file_type: str, user_id: str = None, collection_id: str = None, filename: str = None):
# 更新：
    """生成OSS路径 - 按年/月/日分类"""
# 更新：
    from datetime import datetime
# 更新：
    now = datetime.now()
# 更新：
    year = now.strftime("%Y")
# 更新：
    month = now.strftime("%m")
# 更新：
    day = now.strftime("%d")
# 更新：
    
# 更新：
    if file_type == "temp":
# 更新：
        # 临时文件: temp/{year}/{month}/{day}/{uuid}.{ext}
# 更新：
        import uuid
# 更新：
        unique_id = str(uuid.uuid4())
# 更新：
        ext = filename.split('.')[-1] if filename and '.' in filename else 'jpg'
# 更新：
        return f"temp/{year}/{month}/{day}/{unique_id}.{ext}", unique_id
# 更新：
    
# 更新：
    elif file_type == "collection":
# 更新：
        # 藏品文件: collections/{user_id}/{year}/{collection_id}/{filename}
# 更新：
        if not user_id or not collection_id:
# 更新：
            raise ValueError("user_id and collection_id required for collection")
# 更新：
        return f"collections/{user_id}/{year}/{collection_id}/{filename}"
# 更新：
    
# 更新：
    elif file_type == "avatar":
# 更新：
        # 头像: avatars/{user_id}/avatar.{ext}
# 更新：
        if not user_id:
# 更新：
            raise ValueError("user_id required for avatar")
# 更新：
        ext = filename.split('.')[-1] if filename and '.' in filename else 'jpg'
# 更新：
        return f"avatars/{user_id}/avatar.{ext}"
# 更新：
    
# 更新：
    return None
# 更新：

# 更新：

# 更新：
# 上传图片到OSS - 使用服务层（带压缩）
# 更新：
from app.services.oss import upload_to_oss as oss_upload
# 更新：

# 更新：
def upload_to_oss(file_data, oss_key):
# 更新：
    """上传文件到阿里云OSS（带自动压缩）"""
# 更新：
    return oss_upload(file_data, oss_key)
# 更新：

# 更新：
# 专业提示词
# 更新：
PROFESSIONAL_PROMPT = """你是一名专业的人民币生肖纪念钞鉴定专家。请严格按照以下步骤分析这张图片，并提取准确信息。
# 更新：

# 更新：
【识别流程】
# 更新：
1. 判断类型是否评级钞：首先确认是否为裸钞还是评级钞（有封装盒和标签）
# 更新：
2. 验证纪念钞特征：对照生肖纪念钞特征进行确认
# 更新：
3. 验证评级类型：有'标十'字眼的为标十，有'百连'字眼的为标百，其他为单张
# 更新：
4. 提取信息：仔细阅读标签上的所有文字内容
# 更新：

# 更新：
【版别格式要求】
# 更新：
只需要：年份 + 属相，例如：
# 更新：
- 2024 龙
# 更新：
- 2025 蛇
# 更新：
- 2026 马
# 更新：

# 更新：
【输出要求】
# 更新：
严格按照以下格式输出，每个字段必须填写具体值：
# 更新：
✅ 1 发行机构：中国人民银行
# 更新：
✅ 2 发行版别：2024 龙
# 更新：
✅ 3 面额：贰拾圆
# 更新：
✅ 4 是否评级：是/否
# 更新：
✅ 5 封装类型：裸钞/单张/标十/标百
# 更新：
✅ 6 冠字序号：J0xxxxxxxx
# 更新：
✅ 7 评级机构：ACG/PCGS/PMG
# 更新：
✅ 8 评级分数：67/68/69
# 更新：
✅ 9 是否三星：是/否
# 更新：
✅ 10 特殊标识：金山标/天马标/红绳版等
# 更新：
✅ 11 号码特征：金山号 2 张，天马号 3 张等
# 更新：

# 更新：
现在请仔细分析提供的图片，按上述格式输出结果。"""
# 更新：

# 更新：

# 更新：
@router.post("/recognize")
# 更新：
async def recognize_image(
# 更新：
    image: UploadFile = File(...),
# 更新：
    current_user: User = Depends(get_current_user),
# 更新：
    db: Session = Depends(get_db)
# 更新：
):
# 更新：
    """OCR 图片识别 - 识别后自动保存图片到OSS临时目录"""
# 更新：
    try:
# 更新：
        # 读取图片数据
# 更新：
        image_data = await image.read()
# 更新：
        image_base64 = base64.b64encode(image_data).decode('utf-8')
# 更新：
        
# 更新：
        # 生成OSS临时路径: temp/{year}/{month}/{day}/{uuid}.{ext}
# 更新：
        oss_key, temp_id = get_oss_path("temp", filename=image.filename)
# 更新：
        
# 更新：
        # 初始化temp_path为空
# 更新：
        temp_path = None
# 更新：
        
# 更新：
        # 上传到OSS
# 更新：
        try:
# 更新：
            image_url = upload_to_oss(image_data, oss_key)
# 更新：
        except Exception as oss_err:
# 更新：
            # OSS失败时保存到本地作为备选
# 更新：
            temp_path = os.path.join(UPLOAD_DIR, oss_key.split('/')[-1])
# 更新：
            os.makedirs(os.path.dirname(temp_path), exist_ok=True)
# 更新：
            with open(temp_path, 'wb') as f:
# 更新：
                f.write(image_data)
# 更新：
            image_url = f"/uploads/temp/{oss_key.split('/')[-1]}"
# 更新：
        
# 更新：
        headers = {
# 更新：
            "Authorization": f"Bearer {DASHSCOPE_API_KEY}",
# 更新：
            "Content-Type": "application/json"
# 更新：
        }
# 更新：
        
# 更新：
        # 阿里云 DashScope API 格式 (qwen-vl-plus 视觉模型)
# 更新：
        payload = {
# 更新：
            "model": "qwen-vl-plus",
# 更新：
            "input": {
# 更新：
                "messages": [{
# 更新：
                    "role": "user",
# 更新：
                    "content": [
# 更新：
                        {
# 更新：
                            "image": f"data:{image.content_type};base64,{image_base64}"
# 更新：
                        },
# 更新：
                        {
# 更新：
                            "text": PROFESSIONAL_PROMPT
# 更新：
                        }
# 更新：
                    ]
# 更新：
                }]
# 更新：
            },
# 更新：
            "parameters": {
# 更新：
                "max_tokens": 1000
# 更新：
            }
# 更新：
        }
# 更新：
        
# 更新：
        async with httpx.AsyncClient(timeout=60.0) as client:
# 更新：
            response = await client.post(
# 更新：
                "https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
# 更新：
                json=payload,
# 更新：
                headers=headers
# 更新：
            )
# 更新：
            
# 更新：
            if response.status_code != 200:
# 更新：
                # 识别失败，删除临时文件
# 更新：
                if temp_path and os.path.exists(temp_path):
# 更新：
                    os.remove(temp_path)
# 更新：
                raise HTTPException(status_code=500, detail=f"OCR API 调用失败：{response.text[:200]}")
# 更新：
            
# 更新：
            ocr_result = response.json()
# 更新：
            text_content = ""
# 更新：
            # 新版API返回格式
# 更新：
            if "output" in ocr_result and "choices" in ocr_result["output"]:
# 更新：
                choices = ocr_result["output"]["choices"]
# 更新：
                if choices and len(choices) > 0:
# 更新：
                    content = choices[0].get("message", {}).get("content", [])
# 更新：
                    if content and len(content) > 0:
# 更新：
                        text_content = content[0].get("text", "")
# 更新：
            
# 更新：
            fields = extract_fields(text_content)
# 更新：
            
# 更新：
            # 更新用户AI识别次数
# 更新：
            current_user.f99_95_ai_count = (current_user.f99_95_ai_count or 0) + 1
# 更新：
            db.commit()
# 更新：
            
# 更新：
            # 返回识别结果和临时图片路径
# 更新：
            return {
# 更新：
                "success": True,
# 更新：
                "text": text_content,
# 更新：
                "fields": fields,
# 更新：
                "aiCount": current_user.f99_95_ai_count,
# 更新：
                "temp_image": {
# 更新：
                    "id": temp_id,
# 更新：
                    "filename": oss_key.split('/')[-1],
# 更新：
                    "path": image_url,
# 更新：
                    "original_name": image.filename,
# 更新：
                    "is_oss": image_url.startswith("https://")
# 更新：
                }
# 更新：
            }
# 更新：
            
# 更新：
    except Exception as e:
# 更新：
        import traceback
# 更新：
        error_detail = f"识别失败：{str(e)}\n{traceback.format_exc()}"
# 更新：
        raise HTTPException(status_code=500, detail=error_detail)
# 更新：

# 更新：

# 更新：
def extract_fields(text: str) -> dict:
# 更新：
    """从 OCR 文本中提取字段 - 直接返回 AI 识别结果"""
# 更新：
    import re
# 更新：
    fields = {}
# 更新：
    
# 更新：
    # 解析结构化输出
# 更新：
    patterns = {
# 更新：
        'issuer': r'✅.*?1.*?发行机构.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'version': r'✅.*?2.*?发行版别.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'denomination': r'✅.*?3.*?面额.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'is_graded_text': r'✅.*?4.*?是否评级.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'packaging': r'✅.*?5.*?封装类型.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'prefix_serial': r'✅.*?6.*?冠字序号.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'grading_company': r'✅.*?7.*?评级机构.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'grading_score': r'✅.*?8.*?评级分数.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'three_star_text': r'✅.*?9.*?是否三星.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'special_mark': r'✅.*?10.*?特殊标识.*?[:：]\s*(.+?)(?:\n|$)',
# 更新：
        'serial_feature': r'✅.*?11.*?号码特征.*?[:：]\s*(.+?)(?:\n|$)'
# 更新：
    }
# 更新：
    
# 更新：
    for field, pattern in patterns.items():
# 更新：
        match = re.search(pattern, text, re.IGNORECASE | re.DOTALL)
# 更新：
        if match:
# 更新：
            value = match.group(1).strip()
# 更新：
            # 保留所有值，包括"无"和"未识别"，让前端处理
# 更新：
            fields[field] = value
# 更新：
    
# 更新：
    # 处理是否评级
# 更新：
    if 'is_graded_text' in fields:
# 更新：
        fields['is_graded'] = '是' in fields.pop('is_graded_text')
# 更新：
    
# 更新：
    # 处理是否三星
# 更新：
    if 'three_star_text' in fields:
# 更新：
        fields['three_star'] = '是' in fields.pop('three_star_text')
# 更新：
    
# 更新：
    # 简化版别字段（2024 龙年贺岁纪念钞（标十） → 2024 龙）
# 更新：
    if 'version' in fields:
# 更新：
        version = fields['version']
# 更新：
        # 提取年份和生肖
# 更新：
        year_match = re.search(r'(20\d{2})', version)
# 更新：
        animal = ''
# 更新：
        if '龙' in version:
# 更新：
            animal = '龙'
# 更新：
        elif '蛇' in version:
# 更新：
            animal = '蛇'
# 更新：
        elif '马' in version:
# 更新：
            animal = '马'
# 更新：
        elif '羊' in version:
# 更新：
            animal = '羊'
# 更新：
        elif '猴' in version:
# 更新：
            animal = '猴'
# 更新：
        elif '鸡' in version:
# 更新：
            animal = '鸡'
# 更新：
        elif '狗' in version:
# 更新：
            animal = '狗'
# 更新：
        elif '猪' in version:
# 更新：
            animal = '猪'
# 更新：
        elif '鼠' in version:
# 更新：
            animal = '鼠'
# 更新：
        elif '牛' in version:
# 更新：
            animal = '牛'
# 更新：
        elif '虎' in version:
# 更新：
            animal = '虎'
# 更新：
        elif '兔' in version:
# 更新：
            animal = '兔'
# 更新：
        
# 更新：
        if year_match and animal:
# 更新：
            fields['version'] = f"{year_match.group(1)}{animal}"
# 更新：
    
# 更新：
    return fields
# 更新：

# 更新：

# 更新：
@router.post("/claim-temp-image")
# 更新：
async def claim_temp_image(
# 更新：
    temp_id: str,
# 更新：
    collection_id: str,
# 更新：
    current_user: User = Depends(get_current_user),
# 更新：
    db: Session = Depends(get_db)
# 更新：
):
# 更新：
    """将临时图片移动到正式藏品目录 - 按用户/年/藏品ID分类"""
# 更新：
    from app.models.models import Collection, CollectionImage
# 更新：
    from datetime import datetime
# 更新：
    
# 更新：
    # 验证藏品是否存在
# 更新：
    collection = db.query(Collection).filter(
# 更新：
        Collection.f99_90_id == collection_id,
# 更新：
        Collection.f99_91_user_id == current_user.f99_90_id
# 更新：
    ).first()
# 更新：
    
# 更新：
    if not collection:
# 更新：
        raise HTTPException(status_code=404, detail="藏品不存在")
# 更新：
    
# 更新：
    # 生成正式文件名和OSS路径: collections/{user_id}/{year}/{collection_id}/{filename}
# 更新：
    code = collection.f01_02_code or "0000"
# 更新：
    prefix = collection.f02_10_prefix_serial or ""
# 更新：
    username = current_user.f01_01_name
# 更新：
    import time
# 更新：
    final_filename = f"{username}-{code}-{prefix}-{int(time.time())}.jpg"
# 更新：
    oss_key = get_oss_path("collection", user_id=current_user.f99_90_id, collection_id=collection_id, filename=final_filename)
# 更新：
    
# 更新：
    # 尝试从OSS获取临时图片 - 尝试多种扩展名和日期路径
# 更新：
    temp_extensions = ['jpg', 'jpeg', 'png', 'gif', 'JPG', 'JPEG', 'PNG', 'GIF']
# 更新：
    temp_content = None
# 更新：
    found_key = None
# 更新：
    
# 更新：
    # 尝试最近7天的路径
# 更新：
    from datetime import timedelta
# 更新：
    for i in range(7):
# 更新：
        date = datetime.now() - timedelta(days=i)
# 更新：
        year = date.strftime("%Y")
# 更新：
        month = date.strftime("%m")
# 更新：
        day = date.strftime("%d")
# 更新：
        
# 更新：
        for ext in temp_extensions:
# 更新：
            try:
# 更新：
                temp_oss_key = f"temp/{year}/{month}/{day}/{temp_id}.{ext}"
# 更新：
                import oss2
# 更新：
                auth = oss2.Auth(OSS_CONFIG["access_key_id"], OSS_CONFIG["access_key_secret"])
# 更新：
                bucket = oss2.Bucket(auth, OSS_CONFIG["endpoint"], OSS_CONFIG["bucket_name"])
# 更新：
                temp_content = bucket.get_object(temp_oss_key).read()
# 更新：
                found_key = temp_oss_key
# 更新：
                break
# 更新：
            except:
# 更新：
                continue
# 更新：
        if temp_content:
# 更新：
            break
# 更新：
    
# 更新：
    if temp_content:
# 更新：
        # 上传到正式目录
# 更新：
        bucket.put_object(oss_key, temp_content)
# 更新：
        
# 更新：
        # 删除临时图片
# 更新：
        try:
# 更新：
            bucket.delete_object(found_key)
# 更新：
        except:
# 更新：
            pass
# 更新：
        
# 更新：
        # OSS URL
# 更新：
        image_path = f"{OSS_CONFIG['public_url']}/{oss_key}"
# 更新：
        
# 更新：
    else:
# 更新：
        # OSS失败，使用本地文件
# 更新：
        temp_path = None
# 更新：
        for ext in temp_extensions:
# 更新：
            temp_path = os.path.join(UPLOAD_DIR, f"{temp_id}.{ext}")
# 更新：
            if os.path.exists(temp_path):
# 更新：
                break
# 更新：
        
# 更新：
        if not temp_path or not os.path.exists(temp_path):
# 更新：
            raise HTTPException(status_code=404, detail="临时图片不存在或已过期")
# 更新：
        
# 更新：
        # 保存到本地
# 更新：
        collection_dir = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), "uploads", "collections")
# 更新：
        os.makedirs(collection_dir, exist_ok=True)
# 更新：
        
# 更新：
        new_path = os.path.join(collection_dir, final_filename)
# 更新：
        import shutil
# 更新：
        shutil.move(temp_path, new_path)
# 更新：
        image_path = f"uploads/collections/{final_filename}"
# 更新：
    
# 更新：
    # 创建图片记录
# 更新：
    image_record = CollectionImage(
# 更新：
        id=str(uuid.uuid4()),
# 更新：
        collection_id=collection.f99_90_id,
# 更新：
        filename=final_filename,
# 更新：
        original_name=temp_id,
# 更新：
        path=image_path
# 更新：
    )
# 更新：
    db.add(image_record)
# 更新：
    db.commit()
# 更新：
    
# 更新：
    return {
# 更新：
        "success": True,
# 更新：
        "image": {
# 更新：
            "id": image_record.id,
# 更新：
            "filename": image_record.filename,
# 更新：
            "path": image_record.path
# 更新：
        }
# 更新：
    }
# 更新：
