from __future__ import annotations import uuid from dataclasses import dataclass, field from typing import Any from shared.base_entity import BaseEntity from shared.types import ChartType from src.domain.entities.field_binding import FieldBinding from src.domain.entities.style_config import StyleConfig @dataclass class ChartInstance(BaseEntity): """Aggregate root representing a configured chart.""" dataset_id: uuid.UUID = field(default_factory=uuid.uuid4) chart_type: ChartType = ChartType.BAR bindings: list[FieldBinding] = field(default_factory=list) style: StyleConfig = field(default_factory=dict) filters: list[dict[str, Any]] = field(default_factory=list) sort_config: dict[str, Any] | None = None top_n: int | None = None # ------------------------------------------------------------------ # Domain behaviour # ------------------------------------------------------------------ def update_bindings(self, bindings: list[FieldBinding]) -> None: """Replace current bindings after validation.""" errors = self.validate_bindings(bindings) if errors: raise ValueError("; ".join(errors)) self.bindings = bindings self.touch() def update_style(self, style: StyleConfig) -> None: """Merge new style properties into current style.""" self.style.update(style) self.touch() def validate_bindings(self, bindings: list[FieldBinding] | None = None) -> list[str]: """Return a list of validation error messages (empty means valid).""" from src.domain.services.binding_validation import validate_bindings as _validate target = bindings if bindings is not None else self.bindings return _validate(self.chart_type, target)