"""Component IR — 시스템의 SSOT. LLM 출력/프론트 상태/렌더러/익스포터가 공유한다.

데이터 매핑 모델:
- fields[]      : 단일 슬롯(제목/기준일/부서 등). manual 값 또는 단일 컬럼 매핑.
- collection    : 표/차트용 행 데이터. source(table) + filter(검색컬럼/값) + columns(결과컬럼 → 표시라벨).
"""
from __future__ import annotations

from typing import Any, Literal, Optional

from pydantic import BaseModel, Field

COMPONENT_TYPES = (
    "background_image",
    "title",
    "metadata_text",
    "section_heading",
    "paragraph",
    "bullet_list",
    "line_chart",
    "bar_chart",
    "pie_chart",
    "metric_table",
    "holdings_table",
    "table",
    "image",
    "disclaimer",
    "divider",
    "spacer",
)

DATA_TYPES = (
    "string",
    "number",
    "currency",
    "percent",
    "date",
    "text",
)


class Layout(BaseModel):
    x: int = 0
    y: int = 0
    w: int = 12
    h: int = 1


class ColumnRef(BaseModel):
    table: Optional[str] = None
    column: Optional[str] = None


class FieldBinding(BaseModel):
    source: Literal["manual", "column", "columns"] = "manual"
    value: Optional[Any] = None
    table: Optional[str] = None
    column: Optional[str] = None
    refs: list[ColumnRef] = Field(default_factory=list)


class DataField(BaseModel):
    key: str
    label: str = ""
    role: str = "scalar"
    data_type: str = Field(default="string", alias="dataType")
    required: bool = True
    hint: Optional[str] = None
    display_options: dict[str, Any] = Field(default_factory=dict, alias="displayOptions")
    binding: Optional[FieldBinding] = None
    resolved_value: Any = Field(default=None, alias="resolvedValue")

    model_config = {"populate_by_name": True}


class ColumnMapping(BaseModel):
    label: str
    role: str = "value"
    data_type: str = Field(default="string", alias="dataType")
    column: Optional[str] = None
    display_options: dict[str, Any] = Field(default_factory=dict, alias="displayOptions")

    model_config = {"populate_by_name": True}


class Collection(BaseModel):
    enabled: bool = False
    dataset_id: Optional[str] = Field(default=None, alias="datasetId")
    table: Optional[str] = None
    filter_column: Optional[str] = Field(default=None, alias="filterColumn")
    filter_value: Optional[Any] = Field(default=None, alias="filterValue")
    columns: list[ColumnMapping] = Field(default_factory=list)
    order_by: Optional[str] = Field(default=None, alias="orderBy")
    order_dir: Literal["asc", "desc"] = Field(default="asc", alias="orderDir")
    limit: Optional[int] = None
    resolved_rows: list[dict[str, Any]] = Field(default_factory=list, alias="resolvedRows")

    model_config = {"populate_by_name": True}


class DataSpec(BaseModel):
    fields: list[DataField] = Field(default_factory=list)
    collection: Optional[Collection] = None


class BBox(BaseModel):
    x: float = 0.0
    y: float = 0.0
    w: float = 1.0
    h: float = 0.1


class Component(BaseModel):
    id: str
    type: str
    title: Optional[str] = None
    layout: Layout = Field(default_factory=Layout)
    bbox: Optional[BBox] = None
    data_spec: DataSpec = Field(default_factory=DataSpec, alias="dataSpec")
    confidence: float = 1.0

    model_config = {"populate_by_name": True}


class Background(BaseModel):
    id: str = "bg_000"
    type: Literal["background_image"] = "background_image"
    required: bool = True
    source: str = "uploaded_sample_image"
    image_ref: Optional[str] = Field(default=None, alias="imageRef")
    opacity: float = 0.12
    layout: Layout = Field(default_factory=lambda: Layout(x=0, y=0, w=12, h=50))

    model_config = {"populate_by_name": True}


class Grid(BaseModel):
    cols: int = 12
    row_unit: int = Field(default=33, alias="rowUnit")
    gap: int = 8

    model_config = {"populate_by_name": True}


class ReportMeta(BaseModel):
    title: str = ""
    page_size: str = Field(default="A4", alias="pageSize")
    orientation: str = "portrait"
    source_sample: Optional[str] = Field(default=None, alias="sourceSample")

    model_config = {"populate_by_name": True}


class Report(BaseModel):
    meta: ReportMeta = Field(default_factory=ReportMeta)
    background: Background = Field(default_factory=Background)
    grid: Grid = Field(default_factory=Grid)
    components: list[Component] = Field(default_factory=list)
    overall_confidence: float = Field(default=1.0, alias="overallConfidence")

    model_config = {"populate_by_name": True}


class ReportIR(BaseModel):
    schema_version: str = Field(default="1.0", alias="schemaVersion")
    report: Report = Field(default_factory=Report)

    model_config = {"populate_by_name": True}

    def dump(self) -> dict[str, Any]:
        return self.model_dump(by_alias=True, exclude_none=False)
