from typing import Any, Literal

from attrs import field

from pbi_core.attrs import define
from pbi_core.static_files.layout._base_node import LayoutNode
from pbi_core.static_files.layout.sources import Source

from .enums import (
    AcceptsFilterContext,
    BindingType,
    ReferenceScope,
    SectionVisibility,
    VisualInteractionFilterType,
)
from .properties import PageFormattingObjects


@define()
class LinkField(LayoutNode):
    asAggregation: bool = False
    fieldExpr: Any = None


@define()
class SectionRelationship(LayoutNode):
    source: str
    target: str
    type: int


@define()
class SectionConfig(LayoutNode):
    visibility: SectionVisibility = SectionVisibility.VISIBLE
    type: int | None = None
    linkFields: list[LinkField] | None = None
    objects: PageFormattingObjects | None = None
    relationships: list[SectionRelationship] = field(factory=list)
    filterSortOrder: int | None = None


@define()
class BindingParameter(LayoutNode):
    name: str
    """Name of the parameter - unique across the report."""
    boundFilter: str | None = None
    """Name of the filter which this parameter affects."""
    asAggregation: bool = False
    """The parameter should be applied when the field of the filter is aggregated."""
    qnaSingleSelectRequired: bool = False
    """Exactly one instance value should be picked as a filter for this parameter."""
    fieldExpr: Source | None = None
    """Field expression for page binding"""


@define()
class PageBinding(LayoutNode):
    name: str
    """Name of this binding - unique across the report."""
    type: BindingType
    """Specific usage of this binding (for example drillthrough)."""
    referenceScope: ReferenceScope | None = None
    """What is the scope under which the binding applies."""
    parameters: list[BindingParameter] | None = None
    """Additional parameters to apply when the binding is invoked."""
    acceptsFilterContext: AcceptsFilterContext | None = None
    """Should additional filter context flow when applying the binding."""


@define()
class VisualInteraction(LayoutNode):
    source: str
    """Visual name that will be the source of user interaction (selecting data point for example)."""
    target: str
    """Visual name for the target of the interaction (selecting data point for example)."""
    type: VisualInteractionFilterType
    """How should the interaction flow from source to target visual (as highlights, as filter, none)."""


@define()
class QuickExploreVisualContainerConfig(LayoutNode):
    name: str
    """Name of the visual - matches the name property in visual.json files"""
    fields: list[Source]
    """Specific data fields used to build this visual from the full set of selected fields"""


@define()
class QuickExploreRelatedLayout(LayoutNode):
    version: Literal[1] = 1
    dataTableName: str | None = None
    """If data table is shown, then the name of that visual"""


@define()
class QuickExploreCombinationLayout(LayoutNode):
    version: Literal[1] = 1
    dataTableName: str | None = None
    """If data table is shown, then the name of that visual"""


@define()
class QuickExploreLayoutContainer(LayoutNode):
    related: QuickExploreRelatedLayout
    """A layout that has 1 hero visual and some related visuals"""
    combination: QuickExploreCombinationLayout
    """Layout that generates visuals purely based on combination of fields
    Deprecated: Use related layout instead."""


@define()
class AutoPageGenerationConfig(LayoutNode):
    selectedFields: list[Source]
    """Data fields to use for the auto page generation"""
    visualContainerConfigurations: list[QuickExploreVisualContainerConfig]
    """Visuals already on the page previously generated by the auto-config"""
    layout: QuickExploreLayoutContainer | None = None
    """The specific layout chosen to render the auto-visuals"""


@define()
class Annotation(LayoutNode):
    name: str
    """Unique name for the annotation."""
    value: str
    """A value for this annotation."""
