"""POST /api/v1/parse-image —— iOS 端的截图识别接口。

multipart/form-data:
  - image: file (JPEG/PNG/HEIC,客户端已压到 < 5MB)
  - clusters: str (JSON 序列化的 [{"id":"<uuid>","title":"..."}, ...])
  - package_name: str (可选,iOS 通常不传或传 null;Mock 实现不使用)

占坑期实现接 mock_extractor;后续接真模型时只改这里调 extractor_vision。
"""
from __future__ import annotations

import json
import logging
from typing import Optional

from fastapi import APIRouter, File, Form, HTTPException, UploadFile
from pydantic import ValidationError

from app.mock_extractor import mock_extract_image
from app.schemas import ClusterDtoStr, ParseStrResponse

logger = logging.getLogger("shagua.parse_image")

router = APIRouter(prefix="/api/v1")

# 单张图片上限 5MB。客户端正常压完短边 1024 的 JPEG 通常 < 500KB,
# 超过 5MB 是异常情况(原图 / 客户端压缩 bug),直接拒掉省 LLM 钱。
MAX_IMAGE_BYTES = 5 * 1024 * 1024


@router.post("/parse-image", response_model=ParseStrResponse)
async def parse_image(
    image: UploadFile = File(...),
    clusters: str = Form("[]"),
    package_name: Optional[str] = Form(None),
) -> ParseStrResponse:
    image_bytes = await image.read()
    if not image_bytes:
        logger.info("parse_image rejected: no_image bytes=0")
        raise HTTPException(status_code=422, detail="no_image")
    if len(image_bytes) > MAX_IMAGE_BYTES:
        logger.info("parse_image rejected: image_too_large bytes=%d", len(image_bytes))
        raise HTTPException(status_code=422, detail="image_too_large")

    try:
        clusters_raw = json.loads(clusters) if clusters else []
        if not isinstance(clusters_raw, list):
            raise ValueError("clusters must be a JSON array")
        cluster_dtos = [ClusterDtoStr(**c) for c in clusters_raw]
    except (json.JSONDecodeError, ValueError, TypeError, ValidationError) as e:
        # ValidationError 不是 ValueError 子类(pydantic v2),必须显式 catch,
        # 否则客户端发字段不全的 cluster 会让接口冒 500
        logger.info("parse_image rejected: invalid_clusters err=%s", e)
        raise HTTPException(status_code=422, detail="invalid_clusters")

    logger.info(
        "parse_image request bytes=%d package=%s clusters=%d",
        len(image_bytes),
        package_name,
        len(cluster_dtos),
    )

    result = await mock_extract_image(image_bytes, cluster_dtos)
    if not result.get("success"):
        raise HTTPException(status_code=422, detail=result.get("reason", "extract_failed"))

    return ParseStrResponse(
        title=result["title"],
        price=result["price"],
        source_app=result["source_app"],
        cluster_id=result.get("cluster_id"),
        typical_price=result["typical_price"],
    )
