import argparse
import base64
import json
import os
import urllib.error
import urllib.request

import sophnet_tools

API_URL = "https://www.sophnet.com/api/open-apis/v1/chat/completions"
MODEL = "Qwen3-VL-235B-A22B-Instruct"

SYSTEM_PROMPT = """你是专业的自然与人文识图辅助助手，回答基于图像特征作审慎判断，不能当成权威鉴定结论。用户会提供一张图片。请先大致判断画面里**最主要的那一样东西**可能属于哪类：植物、动物、真菌、昆虫、矿物、人造物或工艺品、食物、景观地标、纸面或屏幕上的内容、其他——若画面含糊，就明说看不准大类。围绕这一主要主体展开；若有容易混淆的近似种或其他主体，用一两句说明替代可能。

语气必须专业、审慎并留有余地：可使用「根据图像特征判断」「更接近」「可能为」「不能完全排除」「仅凭这张图片暂不宜定到种」等表述；禁止使用「这就是」「肯定是」「百分之百」「一定是」之类绝对措辞。学名、毒性、能否食用、是否保护物种等尤其要保守，拿不准就明确说明，并提醒用户以专业鉴定、权威资料或监管信息为准。

用**简体中文**输出几段自然连贯的话，风格应清晰、可靠、易懂；不要 Markdown 标题（不要 #、##），不要表格，不要用「-」或数字做条目罗列。

内容里尽量自然带到（都只作或然叙述；禁止编造图上没有依据的数字，如具体海拔、精确尺寸）：可能的常见叫法、别名；若判断依据较充分，再提学名和大概分类；常见原产地或分布；就这张照片里看得见的花、叶、毛、斑、姿态等特征；常见生境及与人的关系；毒性、可食性或保护类别等风险信息必须保守表述。若照片模糊、角度受限或关键特征被遮挡，要说明结论卡在哪一级，以及进一步确认时建议补拍哪些细节。

开头不要寒暄，直接给出基于图像特征的判断。收束时简短提醒：以上为图像识别参考，涉及食用、触碰、用药、采收、饲养或保护等级等事项，应以权威资料或专业人士意见为准。"""


def _is_likely_base64(raw: str) -> bool:
    text = raw.strip()
    if not text or any(ch.isspace() for ch in text):
        return False
    try:
        base64.b64decode(text, validate=True)
        return True
    except Exception:
        return False


def normalize_image_input(image_input: str, upload_timeout: int = 30) -> str:
    text = image_input.strip()
    if not text:
        raise ValueError("图片输入不能为空")

    if text.startswith(("http://", "https://")):
        return text

    if text.startswith("data:image/") and ";base64," in text:
        return text

    if os.path.exists(text):
        return sophnet_tools.upload_oss(text, upload_timeout)

    if _is_likely_base64(text):
        return text

    raise ValueError("无法识别图片输入：请传入 URL、base64、data URL 或本地文件路径")


def call_vlm(image_input: str) -> str:
    api_key = sophnet_tools.get_api_key()
    image_url = normalize_image_input(image_input)

    payload = {
        "messages": [
            {"role": "system", "content": SYSTEM_PROMPT},
            {
                "role": "user",
                "content": [
                    {
                        "type": "image_url",
                        "image_url": {"url": image_url},
                    }
                ],
            },
        ],
        "model": MODEL,
        "stream": False,
    }

    body = json.dumps(payload).encode("utf-8")
    request = urllib.request.Request(
        url=API_URL,
        data=body,
        method="POST",
        headers={
            "Content-Type": "application/json",
            "Accept": "application/json",
            "Authorization": f"Bearer {api_key}",
        },
    )

    try:
        with urllib.request.urlopen(request, timeout=120) as response:
            raw = response.read().decode("utf-8")
            data = json.loads(raw)
    except urllib.error.HTTPError as exc:
        detail = exc.read().decode("utf-8", errors="ignore")
        raise RuntimeError(f"HTTPError: {exc.code} {exc.reason} | {detail}") from exc
    except urllib.error.URLError as exc:
        raise RuntimeError(f"URLError: {exc.reason}") from exc

    try:
        return data["choices"][0]["message"]["content"]
    except (KeyError, IndexError, TypeError):
        return json.dumps(data, ensure_ascii=False, indent=2)


def build_parser():
    parser = argparse.ArgumentParser(description="识万物：对图片主体做推测性讲解（非专业鉴定）")
    parser.add_argument(
        "image",
        help="图片输入，支持 http(s) URL、base64、data URL 或本地文件路径",
    )
    return parser


if __name__ == "__main__":
    args = build_parser().parse_args()
    print(call_vlm(args.image))
