"""Translate Pydantic / FastAPI validation errors to Chinese for API 422 responses."""

from __future__ import annotations

import re
from typing import Any

# Pydantic v2 default messages (exact match)
_MSG_EXACT: dict[str, str] = {
    "Field required": "必填",
    "Input should be a valid string": "必须是字符串",
    "Input should be a valid integer": "必须是整数",
    "Input should be a valid number": "必须是数字",
    "Input should be a valid boolean": "必须是布尔值",
    "Input should be a valid dictionary": "必须是对象",
    "Input should be a valid list": "必须是数组",
    "JSON decode error": "JSON 格式无效",
    "Input should be a valid dictionary or object to extract fields from": "请求体必须是 JSON 对象",
    "Input should be a valid UUID": "必须是有效的 UUID",
    "Input should be a valid datetime": "必须是有效的日期时间",
    "Input should be a valid date": "必须是有效的日期",
    "Input should be a valid time": "必须是有效的时间",
    "Input should be a valid url": "必须是有效的 URL",
    "Input should be a valid email address": "必须是有效的邮箱",
}

# Regex patterns for parameterized Pydantic messages (legacy / string fields)
_MSG_PATTERNS: list[tuple[re.Pattern[str], str]] = [
    (re.compile(r"^String should have at least (\d+) characters?$"), r"至少需要 \1 个字符"),
    (re.compile(r"^String should have at most (\d+) characters?$"), r"最多 \1 个字符"),
    (re.compile(r"^String should match pattern '(.+)'$"), r"格式不符合要求"),
    (re.compile(r"^Input should be greater than or equal to (\d+)"), r"不能小于 \1"),
    (re.compile(r"^Input should be less than or equal to (\d+)"), r"不能大于 \1"),
    (re.compile(r"^Input should be greater than (\d+)"), r"必须大于 \1"),
    (re.compile(r"^Input should be less than (\d+)"), r"必须小于 \1"),
    (re.compile(r"^List should have at least (\d+) items?(?: .*)?$"), r"至少需要 \1 项"),
    (re.compile(r"^List should have at most (\d+) items?(?: after validation, not (\d+))?$"), r"最多 \1 项"),
]


def translate_validation_msg(msg: str, err_type: str = "", ctx: dict[str, Any] | None = None) -> str:
    """Return Chinese message when a known translation exists."""
    ctx = ctx or {}
    if err_type == "too_long" and ctx.get("field_type") == "List":
        max_len = ctx.get("max_length")
        actual = ctx.get("actual_length")
        if max_len is not None and actual is not None:
            return f"最多 {max_len} 项，当前 {actual} 项"
        if max_len is not None:
            return f"最多 {max_len} 项"
    if err_type == "too_short" and ctx.get("field_type") == "List":
        min_len = ctx.get("min_length")
        actual = ctx.get("actual_length")
        if min_len is not None and actual is not None:
            return f"至少需要 {min_len} 项，当前 {actual} 项"
        if min_len is not None:
            return f"至少需要 {min_len} 项"
    if err_type == "missing":
        return "必填"
    if not msg:
        return msg
    if msg in _MSG_EXACT:
        return _MSG_EXACT[msg]
    for pattern, repl in _MSG_PATTERNS:
        if pattern.match(msg):
            out = pattern.sub(repl, msg)
            # too_long list with "not N" suffix — append actual count when present
            m = re.search(r"not (\d+)$", msg)
            if m and "最多" in out and "当前" not in out:
                return f"{out}，当前 {m.group(1)} 项"
            return out
    if msg.startswith("Value error, "):
        return msg[len("Value error, "):]
    if err_type == "string_type":
        return "必须是字符串"
    if err_type in ("int_type", "int_parsing"):
        return "必须是整数"
    if err_type in ("float_type", "float_parsing"):
        return "必须是数字"
    if err_type == "bool_parsing":
        return "必须是 true 或 false"
    if err_type == "less_than_equal" and ctx.get("le") is not None:
        return f"不能大于 {ctx['le']}"
    if err_type == "greater_than_equal" and ctx.get("ge") is not None:
        return f"不能小于 {ctx['ge']}"
    if err_type == "less_than" and ctx.get("lt") is not None:
        return f"必须小于 {ctx['lt']}"
    if err_type == "greater_than" and ctx.get("gt") is not None:
        return f"必须大于 {ctx['gt']}"
    return msg


_LOC_PREFIX_SKIP = frozenset({"query", "body", "path", "header", "cookie"})

_FIELD_ZH: dict[str, str] = {
    "per_page": "每页条数",
    "page": "页码",
    "limit": "条数",
    "offset": "偏移量",
    "server_ids": "服务器",
    "source_path": "源路径",
    "target_path": "目标路径",
    "sync_mode": "同步模式",
    "batch_id": "批次 ID",
    "batch_size": "批次大小",
    "concurrency": "并发数",
    "value": "值",
    "username": "用户名",
    "password": "密码",
    "name": "名称",
    "cron_expr": "Cron 表达式",
    "fire_at": "执行时间",
}


def translate_loc_zh(loc: Any) -> str:
    """Human-readable Chinese field path (no query/body prefixes)."""
    if not isinstance(loc, (list, tuple)):
        return "参数"
    parts: list[str] = []
    for piece in loc:
        if isinstance(piece, str) and piece in _LOC_PREFIX_SKIP:
            continue
        if isinstance(piece, int):
            parts.append(f"第 {piece + 1} 项")
            continue
        key = str(piece)
        parts.append(_FIELD_ZH.get(key, key))
    return ".".join(parts) if parts else "参数"


def translate_validation_errors(errors: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """Clone Pydantic error dicts with translated ``msg`` and ``loc_zh``."""
    translated: list[dict[str, Any]] = []
    for err in errors:
        ctx = err.get("ctx") if isinstance(err.get("ctx"), dict) else None
        item = {k: v for k, v in err.items() if k != "ctx"}
        item["msg"] = translate_validation_msg(
            str(err.get("msg", "")),
            str(err.get("type", "")),
            ctx,
        )
        item["loc_zh"] = translate_loc_zh(err.get("loc"))
        translated.append(item)
    return translated
