"""Resolver — IR 의 fields(스칼라) / collection(표·차트) 바인딩을 데모 데이터로 결정론적 주입."""
from __future__ import annotations

from typing import Any

from app.services.catalog import get_product


def _rows(product: dict[str, Any], table: str | None) -> list[dict[str, Any]]:
    if not table:
        return []
    val = product.get(table)
    if isinstance(val, list):
        return val
    if isinstance(val, dict):
        return [val]
    return []


def _apply_filter(rows: list[dict[str, Any]], col: str | None, value: Any) -> list[dict[str, Any]]:
    if not col or value is None:
        return rows
    matched = [r for r in rows if str(r.get(col)) == str(value)]
    return matched if matched else rows


def _lookup_column(product: dict[str, Any], table: str | None, col: str | None) -> Any:
    if not table or not col:
        return None
    for r in _rows(product, table):
        if col in r and r.get(col) is not None:
            return r.get(col)
    return None


def _resolve_field(field: dict[str, Any], product: dict[str, Any]) -> Any:
    b = field.get("binding") or {}
    src = b.get("source")
    if src == "manual":
        return b.get("value")
    if src == "column":
        return _lookup_column(product, b.get("table"), b.get("column"))
    if src == "columns":
        out: dict[str, Any] = {}
        for ref in b.get("refs", []) or []:
            col = ref.get("column")
            if col:
                out[col] = _lookup_column(product, ref.get("table"), col)
        return out or None
    return None


def _resolve_manual_collection(collection: dict[str, Any]) -> list[dict[str, Any]]:
    """직접 입력 모드: 사용자가 붙여넣어 파싱된 manualRows 를 columns 라벨 순서로 반환."""
    rows = collection.get("manualRows") or []
    columns = collection.get("columns", []) or []
    out: list[dict[str, Any]] = []
    for idx, r in enumerate(rows, start=1):
        record: dict[str, Any] = {}
        for c in columns:
            label = c.get("label", "")
            if c.get("role") == "index" and not c.get("column"):
                record[label] = idx
            else:
                record[label] = r.get(label)
        out.append(record)
    return out


def _resolve_collection(collection: dict[str, Any], product: dict[str, Any]) -> list[dict[str, Any]]:
    if collection.get("source") == "manual":
        return _resolve_manual_collection(collection)
    rows = _rows(product, collection.get("table"))
    rows = _apply_filter(rows, collection.get("filterColumn"), collection.get("filterValue"))

    order_by = collection.get("orderBy")
    if order_by:
        desc = (collection.get("orderDir") or "asc") == "desc"
        rows = sorted(rows, key=lambda r: (r.get(order_by) is None, r.get(order_by)), reverse=desc)
    limit = collection.get("limit")
    if isinstance(limit, int) and limit > 0:
        rows = rows[:limit]

    columns = collection.get("columns", []) or []
    out: list[dict[str, Any]] = []
    for idx, r in enumerate(rows, start=1):
        record: dict[str, Any] = {}
        for c in columns:
            label = c.get("label", "")
            col = c.get("column")
            role = c.get("role", "value")
            if role == "index" and not col:
                record[label] = idx
            elif col is not None:
                record[label] = r.get(col)
        out.append(record)
    return out


def resolve(ir: dict[str, Any], fund_code: str) -> dict[str, Any]:
    product = get_product(fund_code) or {}
    warnings: list[str] = []

    for comp in ir.get("report", {}).get("components", []):
        spec = comp.get("dataSpec", {}) or {}
        for field in spec.get("fields", []) or []:
            b = field.get("binding")
            if not b:
                if field.get("required"):
                    warnings.append(f"{comp.get('id')}.{field.get('key')} 미바인딩")
                continue
            field["resolvedValue"] = _resolve_field(field, product)

        collection = spec.get("collection")
        if collection and collection.get("enabled"):
            collection["resolvedRows"] = _resolve_collection(collection, product)
            if not collection["resolvedRows"]:
                warnings.append(f"{comp.get('id')} collection 결과 없음(테이블/컬럼 매핑 확인)")

    ir.setdefault("_meta", {})["warnings"] = warnings
    return ir
