# -*- coding: utf-8 -*-
"""
schema.py：QuoteData 数据契约、校验器、归一化。

所有模板共用同一契约，沿用 reports4 的 SAMPLE 字段结构。
"""
import copy

# ── 缺省值表 ──────────────────────────────────────────────
DEFAULTS = {
    "layout_title": "整线布局尺寸图",
    "show_layout": True,
}

# 中文数字扩展（用于缺 index 时自动补）
_CN_DIGITS = [
    "一", "二", "三", "四", "五", "六", "七", "八", "九", "十",
    "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
]

# 设备之前的固定章节数：一 公司简介、二 客户要求及分析、三 布局图
_SECTION_OFFSET = 3


def validate(data):
    """校验 QuoteData 结构，返回错误信息列表（空=通过）。

    检查:
        - 必填顶层字段: project_title, contact_person, contact_phone, requirements
        - equipments 元素: 必须有 name
        - quote_items 元素: 必须有 name
        - features 元素: 必须有 title 和 lines
        - params 元素: 必须有 k 和 v
    """
    errors = []

    for field in ("project_title", "contact_person", "contact_phone", "requirements"):
        val = data.get(field)
        if val is None or (isinstance(val, str) and val.strip() == ""):
            errors.append(f"必填字段缺失或为空: {field}")

    # equipments
    eqs = data.get("equipments", [])
    if not isinstance(eqs, list):
        errors.append("equipments 必须为列表")
    else:
        for i, eq in enumerate(eqs):
            if not isinstance(eq, dict):
                errors.append(f"equipments[{i}] 必须为对象")
                continue
            if not eq.get("name"):
                errors.append(f"equipments[{i}] 缺少 name")
            # features 子元素
            for j, feat in enumerate(eq.get("features", [])):
                if not isinstance(feat, dict):
                    errors.append(f"equipments[{i}].features[{j}] 必须为对象")
                    continue
                if not feat.get("title"):
                    errors.append(f"equipments[{i}].features[{j}] 缺少 title")
                if not feat.get("lines") or not isinstance(feat.get("lines"), list):
                    errors.append(f"equipments[{i}].features[{j}] 缺少 lines 或类型错误")
            # params 子元素
            for j, p in enumerate(eq.get("params", [])):
                if not isinstance(p, dict):
                    errors.append(f"equipments[{i}].params[{j}] 必须为对象")
                    continue
                if not p.get("k") or not p.get("v"):
                    errors.append(f"equipments[{i}].params[{j}] 缺少 k 或 v")

    # quote_items
    items = data.get("quote_items", [])
    if not isinstance(items, list):
        errors.append("quote_items 必须为列表")
    else:
        for i, it in enumerate(items):
            if not isinstance(it, dict):
                errors.append(f"quote_items[{i}] 必须为对象")
                continue
            if not it.get("name"):
                errors.append(f"quote_items[{i}] 缺少 name")

    return errors


def normalize(data):
    """归一化数据：填缺省值、requirements list->str、index 缺失时自动补。

    不修改入参，返回新副本。
    """
    d = copy.deepcopy(data)

    # requirements: list -> str
    req = d.get("requirements")
    if isinstance(req, (list, tuple)):
        d["requirements"] = "\n".join(str(x) for x in req)

    # 缺省值
    for k, v in DEFAULTS.items():
        if k not in d:
            d[k] = v

    # equipment index 自动补（从"四"开始，前面三节是公司简介/客户要求/布局图）
    for i, eq in enumerate(d.get("equipments", [])):
        if not eq.get("index"):
            idx = i + 1 + _SECTION_OFFSET
            eq["index"] = _CN_DIGITS[idx - 1] if idx <= len(_CN_DIGITS) else str(idx)

    # quote_items 缺 image 给空串
    for it in d.get("quote_items", []):
        it.setdefault("image", "")

    # equipments 缺 images 给 [""]
    for eq in d.get("equipments", []):
        eq.setdefault("images", [""])

    # 计算后续章节的动态序号：报价表 & 售后服务
    eq_count = len(d.get("equipments", []))
    quote_idx = eq_count + _SECTION_OFFSET + 1
    after_sales_idx = eq_count + _SECTION_OFFSET + 2
    d["section_quote_table"] = _CN_DIGITS[quote_idx - 1] if quote_idx <= len(_CN_DIGITS) else str(quote_idx)
    d["section_after_sales"] = _CN_DIGITS[after_sales_idx - 1] if after_sales_idx <= len(_CN_DIGITS) else str(after_sales_idx)

    return d
