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

API_URL = "https://www.sophnet.com/api/open-apis/v1/chat/completions"
MODEL = "Qwen2.5-VL-32B-Instruct"


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": "详细描述图片中的所有内容，包括文字、物体、场景、人物等。直接描述，禁止废话",
            },
            {
                "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

    return _extract_description(data)


def _extract_description(data: dict) -> str:
    error = data.get("error")
    if isinstance(error, dict):
        message = error.get("message")
        if isinstance(message, str) and message.strip():
            raise RuntimeError(f"图片描述失败：API 返回错误：{message.strip()}")
        raise RuntimeError(
            f"图片描述失败：API 返回错误：{json.dumps(error, ensure_ascii=False)}"
        )

    choices = data.get("choices")
    if not isinstance(choices, list) or not choices:
        raise RuntimeError("图片描述失败：模型未返回 choices")

    first = choices[0]
    if not isinstance(first, dict):
        raise RuntimeError("图片描述失败：choices[0] 格式异常")

    message = first.get("message")
    if not isinstance(message, dict):
        raise RuntimeError("图片描述失败：响应中缺少 message")

    content = message.get("content")
    if not isinstance(content, str) or not content.strip():
        raise RuntimeError("图片描述失败：模型返回空描述")

    return content.strip()


def build_parser():
    parser = argparse.ArgumentParser(description="调用 VLM 生成图片描述")
    parser.add_argument(
        "image",
        help="图片输入，支持 http(s) URL、base64、data URL 或本地文件路径",
    )
    return parser


if __name__ == "__main__":
    args = build_parser().parse_args()
    try:
        print(call_vlm(args.image))
    except Exception as e:
        print(str(e), file=sys.stderr)
        sys.exit(1)
