"""범용 뷰모델 — resolved IR 을 타입별 렌더 컴포넌트 배열로 변환.

ETF 전용 구조 가정 없이 컴포넌트를 순회하며, 렌더러/익스포터가 공통으로 쓰는
중립적인 표현(headers/rows, categories/series, fields, bullets, text)으로 정규화한다.
"""
from __future__ import annotations

from typing import Any


def _scalar(v: Any) -> Any:
    if isinstance(v, (dict, list)):
        return ""
    return v


def _fmt_date(raw: Any, fmt: str | None) -> str:
    s = str(raw or "")
    if not fmt or not s:
        return s
    parts = s.replace(".", "-").split("-")
    if len(parts) >= 3:
        y, m, d = parts[0], parts[1], parts[2]
        return fmt.replace("YYYY", y).replace("MM", m).replace("DD", d)
    return s


def _field_value(field: dict[str, Any]) -> Any:
    rv = field.get("resolvedValue")
    if rv is None:
        b = field.get("binding") or {}
        if b.get("source") == "manual":
            rv = b.get("value")
    fmt = (field.get("displayOptions") or {}).get("dateFormat")
    if field.get("dataType") == "date" and fmt:
        return _fmt_date(_scalar(rv), fmt)
    return _scalar(rv)


def _collection_columns_by_role(columns: list[dict[str, Any]], role: str) -> list[dict[str, Any]]:
    return [c for c in columns if c.get("role") == role]


def _table_view(comp: dict[str, Any]) -> dict[str, Any]:
    col = comp.get("dataSpec", {}).get("collection", {}) or {}
    columns = col.get("columns", []) or []
    headers = [c.get("label", "") for c in columns]
    rows_src = col.get("resolvedRows", []) or []
    rows = [[_scalar(r.get(c.get("label", ""))) for c in columns] for r in rows_src]
    return {"headers": headers, "rows": rows}


def _chart_view(comp: dict[str, Any]) -> dict[str, Any]:
    col = comp.get("dataSpec", {}).get("collection", {}) or {}
    columns = col.get("columns", []) or []
    rows = col.get("resolvedRows", []) or []
    x_cols = _collection_columns_by_role(columns, "x")
    label_cols = _collection_columns_by_role(columns, "label")
    cat_col = (x_cols or label_cols or [None])[0]
    cat_label = cat_col.get("label") if cat_col else None
    categories = [_scalar(r.get(cat_label)) for r in rows] if cat_label else []

    series_cols = _collection_columns_by_role(columns, "series")
    value_cols = _collection_columns_by_role(columns, "value")
    if comp.get("type") == "pie_chart":
        vcol = (value_cols or [None])[0]
        values = [_num(r.get(vcol.get("label"))) for r in rows] if vcol else []
        return {"categories": categories, "values": values}
    use_cols = series_cols or value_cols
    series = [
        {"name": c.get("label", ""), "values": [_num(r.get(c.get("label"))) for r in rows]}
        for c in use_cols
    ]
    return {"categories": categories, "series": series}


def _num(v: Any):
    try:
        return float(v)
    except (TypeError, ValueError):
        return v


def build_view(ir: dict[str, Any], page_meta: dict[str, Any] | None = None) -> dict[str, Any]:
    report = ir.get("report", {})
    meta = report.get("meta", {})
    components_out: list[dict[str, Any]] = []

    title = meta.get("title", "")
    for comp in report.get("components", []):
        ctype = comp.get("type")
        title_text = comp.get("title", "")
        _before = len(components_out)
        if ctype in ("title",):
            text = _field_value((comp.get("dataSpec", {}).get("fields") or [{}])[0]) or title_text
            components_out.append({"type": "title", "text": str(text)})
            if text:
                title = str(text)
        elif ctype == "section_heading":
            components_out.append({"type": "section_heading", "text": title_text or ""})
        elif ctype == "metadata_text":
            fields = [
                {"label": f.get("label", ""), "value": str(_field_value(f))}
                for f in comp.get("dataSpec", {}).get("fields", []) or []
            ]
            components_out.append({"type": "metadata_text", "title": title_text, "fields": fields})
        elif ctype in ("paragraph", "disclaimer"):
            fields = comp.get("dataSpec", {}).get("fields", []) or []
            text = str(_field_value(fields[0])) if fields else (title_text or "")
            components_out.append({"type": ctype, "title": title_text, "text": text})
        elif ctype == "bullet_list":
            bullets = comp.get("generatedBullets")
            if not bullets:
                bullets = [
                    str(_field_value(f))
                    for f in comp.get("dataSpec", {}).get("fields", []) or []
                    if _field_value(f)
                ]
            components_out.append({"type": "bullet_list", "title": title_text, "bullets": [str(b) for b in bullets]})
        elif ctype in ("line_chart", "bar_chart", "pie_chart"):
            view = _chart_view(comp)
            components_out.append({"type": ctype, "title": title_text, **view})
        elif ctype in ("metric_table", "holdings_table", "table"):
            view = _table_view(comp)
            components_out.append({"type": ctype, "title": title_text, **view})
        elif ctype in ("divider", "spacer", "image"):
            components_out.append({"type": ctype, "title": title_text})

        bbox = comp.get("bbox") or comp.get("layout")
        if bbox is not None:
            for item in components_out[_before:]:
                item["bbox"] = {
                    "x": bbox.get("x", 0.0),
                    "y": bbox.get("y", 0.0),
                    "w": bbox.get("w", 1.0),
                    "h": bbox.get("h", 0.1),
                }

    return {
        "title": title,
        "brand": (page_meta or {}).get("brand", ""),
        "badge": (page_meta or {}).get("badge", ""),
        "pageSize": meta.get("pageSize", "A4"),
        "orientation": meta.get("orientation", "portrait"),
        "components": components_out,
    }
