"""
Adaptive Card element classes.

Provides the building blocks for constructing Adaptive Cards including
text blocks, images, columns, and tables.
"""

from typing import Any, Dict, List, Optional, cast


class CardElement:
    """
    Base class for all Adaptive Card elements.

    Provides serialization to dictionary format for JSON conversion.
    All card elements should inherit from this class.
    """

    def toDict(self) -> Dict[str, Any]:
        """
        Convert the element and all nested elements to a dictionary.

        Recursively processes nested CardElement objects and lists.
        Skips None values.

        Returns:
            dict: Dictionary representation suitable for JSON serialization
        """
        result = {}
        for key, value in self.__dict__.items():
            if value is None:
                continue  # Skip None values
            elif isinstance(value, CardElement):
                result[key] = value.toDict()
            elif isinstance(value, list):
                result[key] = cast(
                    Any,
                    [item.toDict() if isinstance(item, CardElement) else item for item in value],
                )
            elif isinstance(value, dict):
                result[key] = {
                    k: v.toDict() if isinstance(v, CardElement) else v for k, v in value.items()
                }
            else:
                result[key] = value
        return result


class TextBlock(CardElement):
    """
    Text block element for displaying text in a card.

    Supports various text styling options including size, weight,
    color, and font type.

    Attributes:
        type (str): Always "TextBlock"
        text (str): Text content to display
        wrap (bool): Whether text should wrap
        weight (str): Font weight (e.g., "Bolder", "Lighter")
        size (str): Font size (e.g., "Small", "Medium", "Large", "ExtraLarge")
        color (str): Text color (e.g., "Default", "Warning", "Attention")
        style (str): Text style (e.g., "default", "heading")
        fontType (str): Font type (e.g., "Monospace")

    Examples:
        >>> title = TextBlock("Welcome!", weight="Bolder", size="Large")
        >>> subtitle = TextBlock("This is a subtitle", color="Accent")
    """

    def __init__(
        self,
        text: str,
        wrap: bool = True,
        weight: Optional[str] = None,
        size: Optional[str] = None,
        color: Optional[str] = None,
        style: Optional[str] = "default",
        fontType: Optional[str] = None,
    ):
        """
        Initialize a TextBlock element.

        Args:
            text: Text content to display
            wrap: Whether text should wrap (default True)
            weight: Font weight ("Bolder", "Lighter")
            size: Font size ("Small", "Medium", "Large", "ExtraLarge")
            color: Text color ("Default", "Warning", "Attention", "Accent")
            style: Text style ("default", "heading")
            fontType: Font type ("Default", "Monospace")
        """
        self.type = "TextBlock"
        self.text = text
        self.wrap = wrap
        if weight:
            self.weight = weight
        if size:
            self.size = size
        if color:
            self.color = color
        if style:
            self.style = style
        if fontType:
            self.fontType = fontType


class Badge(CardElement):
    """
    Badge element for displaying a badge/tag in a card.

    Badges are small labels that can include icons and various styles.

    Attributes:
        type (str): Always "Badge"
        text (str): Badge text
        size (str): Badge size
        style (str): Badge style ("Accent", "Default", etc.)
        icon (str): Icon name
        iconPosition (str): Icon position relative to text

    Examples:
        >>> status = Badge("Active", style="Accent", icon="CheckMark")
        >>> env = Badge("Production", style="Warning")
    """

    def __init__(
        self,
        text: str,
        spacing: Optional[str] = None,
        horizontalAlignment: Optional[str] = None,
        size: Optional[str] = None,
        style: Optional[str] = "Accent",
        icon: Optional[str] = None,
        position: Optional[str] = None,
    ):
        """
        Initialize a Badge element.

        Args:
            text: Badge text
            spacing: Spacing around the badge
            horizontalAlignment: Horizontal alignment
            size: Badge size
            style: Badge style ("Accent", "Default", "Subtle")
            icon: Icon name
            position: Icon position ("Before", "After")
        """
        self.type = "Badge"
        self.text = text
        if size:
            self.size = size
        if spacing:
            self.spacing = spacing
        if style:
            self.style = style
        if horizontalAlignment:
            self.horizontalAlignment = horizontalAlignment
        if icon:
            self.icon = icon
        if position:
            self.iconPosition = position


class Image(CardElement):
    """
    Image element for displaying images in a card.

    Attributes:
        type (str): Always "Image"
        url (str): Image URL
        size (str): Image size ("Auto", "Stretch", "Small", "Medium", "Large")
        altText (str): Alternative text for accessibility

    Examples:
        >>> logo = Image("https://example.com/logo.png", size="Medium")
        >>> avatar = Image(url, alt_text="User avatar")
    """

    def __init__(self, url: str, size: Optional[str] = None, alt_text: Optional[str] = None):
        """
        Initialize an Image element.

        Args:
            url: Image URL
            size: Image size ("Auto", "Stretch", "Small", "Medium", "Large")
            alt_text: Alternative text for accessibility
        """
        self.type = "Image"
        self.url = url
        if size:
            self.size = size
        if alt_text:
            self.altText = alt_text


class Column(CardElement):
    """
    Column element for use within a ColumnSet.

    Columns contain a list of items and have configurable width.

    Attributes:
        type (str): Always "Column"
        width (str): Column width ("auto", "stretch", or pixel/weight value)
        items (list): List of card elements in this column

    Examples:
        >>> col = Column([TextBlock("Title"), TextBlock("Subtitle")], width="stretch")
    """

    def __init__(self, content: List, width: Optional[str] = "stretch"):
        """
        Initialize a Column element.

        Args:
            content: List of CardElement objects or dicts
            width: Column width ("auto", "stretch", or specific value)
        """
        self.type = "Column"
        self.width = width
        self.items = [item.to_dict() if isinstance(item, CardElement) else item for item in content]


class ColumnSet(CardElement):
    """
    ColumnSet element for multi-column layouts.

    Contains one or more Column elements arranged horizontally.

    Attributes:
        type (str): Always "ColumnSet"
        columns (list): List of Column elements
        style (str): Optional style for the column set

    Examples:
        >>> cols = ColumnSet()
        >>> cols.addColumn([TextBlock("Left")], width="1")
        >>> cols.addColumn([TextBlock("Right")], width="1")

        >>> # Or with columns directly:
        >>> cols = ColumnSet(columns=[
        ...     Column([TextBlock("A")], width="auto"),
        ...     Column([TextBlock("B")], width="stretch")
        ... ])
    """

    def __init__(self, style: Optional[str] = None, columns: Optional[List] = None):
        """
        Initialize a ColumnSet element.

        Args:
            style: Optional style for the column set
            columns: Optional list of Column elements
        """
        self.type = "ColumnSet"

        # Process columns - convert CardElements to dicts
        if columns:
            self.columns = [
                col.to_dict() if isinstance(col, CardElement) else col for col in columns
            ]
        else:
            self.columns = []

        if style:
            self.style = style

    def addColumn(self, content: List, width: Optional[str] = "stretch") -> None:
        """
        Add a column to this ColumnSet.

        Args:
            content: List of CardElement objects for the column
            width: Column width
        """
        items = [item.to_dict() if isinstance(item, CardElement) else item for item in content]
        self.columns.append({"type": "Column", "width": width, "items": items})


class Table(CardElement):
    """
    Table element for displaying tabular data.

    Supports header rows and data rows with configurable column widths.

    Attributes:
        type (str): Always "Table"
        columns (list): Column definitions with widths
        rows (list): Table rows (header + data)

    Examples:
        >>> table = Table()
        >>> table.setHeaders([
        ...     {"name": "Name", "width": 2},
        ...     {"name": "Status", "width": 1}
        ... ])
        >>> table.addRow(["Item 1", "Active"])
        >>> table.addRow(["Item 2", "Inactive"])
    """

    def __init__(self):
        """Initialize an empty Table element."""
        self.type = "Table"
        self.columns = []
        self.rows = []

    def setHeaders(self, header_data: List[Dict]) -> None:
        """
        Set the table header row.

        Args:
            header_data: List of header definitions, each containing:
                - width (int): Column width weight (default 1)
                - name (str, optional): Header text

        Examples:
            >>> table.setHeaders([
            ...     {"name": "Name", "width": 2},
            ...     {"name": "Value", "width": 1}
            ... ])
        """
        cells = []
        for header in header_data:
            self.columns.append({"width": header.get("width", 1)})

            name = header.get("name")
            if name:
                text_block = TextBlock(text=name, wrap=False, style="heading")
                cells.append({"type": "TableCell", "items": [text_block.to_dict()]})

        if cells:
            self.rows.append({"type": "TableRow", "cells": cells})

    def addRow(self, row_data: List) -> None:
        """
        Add a data row to the table.

        Args:
            row_data: List of cell values (will be converted to strings)

        Examples:
            >>> table.addRow(["John", "Active"])
            >>> table.addRow(["Jane", "Pending"])
        """
        cells = []

        for col in row_data:
            text_block = TextBlock(text=str(col), wrap=True)
            cells.append({"type": "TableCell", "items": [text_block.to_dict()]})

        self.rows.append({"type": "TableRow", "cells": cells})
