"""Base formatter interface for CLI output."""

from abc import ABC, abstractmethod
from dataclasses import dataclass
import json
from typing import TYPE_CHECKING, Any, Literal, cast

if TYPE_CHECKING:
    pass


ColumnAlignment = Literal["left", "center", "right"]


@dataclass
class ColumnMeta:
    """Metadata about a column for formatters that need layout info.

    This is a simplified view of ColumnConfig passed to formatters.
    """

    label: str
    compact_line: int = 1
    compact_style: str | None = None
    width: int | None = None
    align: ColumnAlignment = "left"


class Formatter(ABC):
    """Abstract base class for output formatters.

    Formatters transform data into specific output formats (Rich tables, JSON, YAML, etc.).
    """

    @abstractmethod
    def format_entity(self, data: dict[str, Any], title: str | None = None) -> Any:
        """Format a single entity's details as key-value pairs.

        Args:
            data: Dictionary mapping field labels to values
            title: Optional title for the entity display

        Returns:
            Formatted output (str for JSON, Rich renderable for Rich, etc.)
        """
        pass

    @abstractmethod
    def format_list(
        self,
        items: list[dict[str, Any]],
        title: str | None = None,
        columns: list[ColumnMeta] | None = None,
    ) -> Any:
        """Format a list of items as a table or similar structure.

        Args:
            items: List of dictionaries, each representing one row
            title: Optional title for the list display
            columns: Optional column metadata for layout-aware formatters

        Returns:
            Formatted output (str for JSON, Rich renderable for Rich, etc.)
        """
        pass

    @abstractmethod
    def format_success(self, message: str) -> str:
        """Format a success message.

        Args:
            message: Success message text

        Returns:
            Formatted success message
        """
        pass

    @abstractmethod
    def format_info(self, message: str) -> str:
        """Format an informational message.

        Args:
            message: Info message text

        Returns:
            Formatted info message
        """
        pass

    @abstractmethod
    def format_warning(self, message: str) -> str:
        """Format a warning message.

        Args:
            message: Warning message text

        Returns:
            Formatted warning message
        """
        pass

    @abstractmethod
    def format_result(
        self,
        action: str,
        resource: str,
        status: str = "success",
        identifier: str | None = None,
        name: str | None = None,
        extra_fields: dict[str, Any] | None = None,
    ) -> str:
        """Format a mutation result block.

        Args:
            action: Action performed (create, update, delete)
            resource: Resource type (agent, user, organization)
            status: Result status (success, failed)
            identifier: Optional resource identifier (ID)
            name: Optional resource name
            extra_fields: Optional additional fields to include

        Returns:
            Formatted result block string
        """
        pass


PSQL_RECORD_WIDTH = 46


def format_scalar_value(value: Any) -> str:
    if value is None:
        return "(null)"
    if isinstance(value, str):
        return '""' if value == "" else value
    if isinstance(value, bool):
        return "true" if value else "false"
    if isinstance(value, (list, tuple)):
        sequence = cast(list[Any] | tuple[Any, ...], value)
        return "[]" if not sequence else json.dumps(cast(Any, sequence), default=str)
    if isinstance(value, dict):
        mapping = cast(dict[Any, Any], value)
        return (
            "{}"
            if not mapping
            else json.dumps(cast(Any, mapping), default=str, sort_keys=True)
        )
    return str(value)


def render_psql_records(
    records: list[dict[str, Any]], block_label: str = "RECORD"
) -> str:
    if not records:
        return "(0 rows)"

    lines: list[str] = []
    for index, record in enumerate(records, start=1):
        header = f"-[ {block_label} {index} ]"
        lines.append(header + ("-" * max(0, PSQL_RECORD_WIDTH - len(header))))

        label_width = max((len(label) for label in record), default=0)
        continuation_prefix = (" " * label_width) + "   "

        for label, raw_value in record.items():
            rendered_value = format_scalar_value(raw_value)
            value_lines = rendered_value.split("\n")
            lines.append(f"{label:<{label_width}} : {value_lines[0]}")
            for continuation_line in value_lines[1:]:
                lines.append(f"{continuation_prefix}{continuation_line}")

        lines.append("")

    row_count = len(records)
    row_label = "row" if row_count == 1 else "rows"
    lines.append(f"({row_count} {row_label})")
    return "\n".join(lines)


def render_psql_result(
    action: str,
    resource: str,
    status: str = "success",
    identifier: str | None = None,
    name: str | None = None,
    extra_fields: dict[str, Any] | None = None,
) -> str:
    fields: dict[str, Any] = {
        "Action": action,
        "Resource": resource,
        "Status": status,
    }
    if identifier is not None:
        fields["ID"] = identifier
    if name is not None:
        fields["Name"] = name
    if extra_fields:
        for key, value in extra_fields.items():
            if key not in fields:
                fields[key] = value

    header = "-[ RESULT ]"
    lines = [header + ("-" * max(0, PSQL_RECORD_WIDTH - len(header)))]

    label_width = max((len(label) for label in fields), default=0)
    continuation_prefix = (" " * label_width) + "   "

    for label, raw_value in fields.items():
        rendered_value = format_scalar_value(raw_value)
        value_lines = rendered_value.split("\n")
        lines.append(f"{label:<{label_width}} : {value_lines[0]}")
        for continuation_line in value_lines[1:]:
            lines.append(f"{continuation_prefix}{continuation_line}")

    lines.append("")
    lines.append("(1 row)")
    return "\n".join(lines)
