"""Unified output service for CLI presentation."""

import json
import sys
from enum import Enum
from typing import Any, cast

from pydantic import BaseModel
from rich.console import Console

from cli.infrastructure.console import get_console
from cli.infrastructure.formatters.base import Formatter
from cli.infrastructure.formatters.compact_formatter import CompactFormatter
from cli.infrastructure.formatters.json_formatter import JsonFormatter
from cli.infrastructure.formatters.table_formatter import TableFormatter
from cli.infrastructure.renderers.entity_renderer import EntityRenderer, FieldConfig
from cli.infrastructure.renderers.list_renderer import ColumnConfig, ListRenderer


class OutputFormat(str, Enum):
    """Supported output formats."""

    COMPACT = "compact"
    JSON = "json"
    TABLE = "table"


class OutputService:
    """Unified output service.

    Usage:
        output = OutputService.get()
        output.success("Operation completed")
        output.entity(response, "User Details", USER_FIELDS)
        output.table(items, "Users", USER_COLUMNS)
    """

    _instance: "OutputService | None" = None
    _format: OutputFormat = OutputFormat.COMPACT

    def __init__(
        self,
        format: OutputFormat = OutputFormat.COMPACT,
        console: Console | None = None,
    ):
        """Initialize output service with specified format.

        Args:
            format: Output mode for record blocks or raw JSON payloads
        """
        self.format = format
        self._console = console if console is not None else get_console()
        self._formatter = self._create_formatter(format)
        self._entity_renderer = EntityRenderer(self._formatter)
        self._list_renderer = ListRenderer(self._formatter)

    @classmethod
    def configure(cls, format: OutputFormat) -> None:
        """Configure global output format. Call once at app startup.

        Args:
            format: Output format to use globally
        """
        cls._format = format
        cls._instance = None  # Reset singleton

    @classmethod
    def get(cls) -> "OutputService":
        """Get the configured output service instance.

        Returns:
            Singleton OutputService instance
        """
        if cls._instance is None:
            cls._instance = cls(cls._format)
        return cls._instance

    def _create_formatter(self, format: OutputFormat) -> Formatter:
        """Create formatter instance for the specified format.

        Args:
            format: Output format

        Returns:
            Formatter instance
        """
        if format == OutputFormat.JSON:
            return JsonFormatter()
        if format == OutputFormat.TABLE:
            return TableFormatter()
        return CompactFormatter(self._console)

    def _write_json_message_to_stderr(self, message: str) -> None:
        print(message, file=sys.stderr)

    def _write_raw_stdout(self, content: str) -> None:
        self._console.file.write(f"{content}\n")
        self._console.file.flush()

    def _to_jsonable(self, payload: Any) -> Any:
        if isinstance(payload, BaseModel):
            return payload.model_dump(mode="json")
        if isinstance(payload, dict):
            json_dict = cast(dict[str, Any], payload)
            jsonable_dict: dict[str, Any] = {}
            for key, value in json_dict.items():
                jsonable_dict[str(key)] = self._to_jsonable(value)
            return jsonable_dict
        if isinstance(payload, list):
            json_list = cast(list[Any], payload)
            jsonable_list: list[Any] = []
            for value in json_list:
                jsonable_list.append(self._to_jsonable(value))
            return jsonable_list
        return payload

    def raw(self, payload: BaseModel | dict[str, Any] | list[Any]) -> None:
        jsonable = self._to_jsonable(payload)
        self._write_raw_stdout(json.dumps(jsonable, indent=2, default=str))

    def text(self, content: str) -> None:
        self._console.print(content, markup=True, highlight=False, soft_wrap=False)

    def progress(self, message: str) -> None:
        print(message, file=sys.stderr)

    # Message methods
    def success(self, message: str) -> None:
        """Print a success message.

        Args:
            message: Success message to display
        """
        output = self._formatter.format_success(message)
        if self.format == OutputFormat.JSON:
            self._write_json_message_to_stderr(output)
            return
        self._console.print(output, markup=False, highlight=False)

    def info(self, message: str) -> None:
        """Print an informational message.

        Args:
            message: Info message to display
        """
        output = self._formatter.format_info(message)
        if self.format == OutputFormat.JSON:
            self._write_json_message_to_stderr(output)
            return
        self._console.print(output, markup=False, highlight=False)

    def warning(self, message: str) -> None:
        """Print a warning message.

        Args:
            message: Warning message to display
        """
        output = self._formatter.format_warning(message)
        if self.format == OutputFormat.JSON:
            self._write_json_message_to_stderr(output)
            return
        self._console.print(output, markup=False, highlight=False)

    def status(self, message: str) -> None:
        """Print a status message (dim, no icon).

        Args:
            message: Status message to display
        """
        if self.format == OutputFormat.JSON:
            self._write_json_message_to_stderr(message)
            return
        self._console.print(message, markup=False, highlight=False)

    def result(
        self,
        action: str,
        resource: str,
        status: str = "success",
        identifier: str | None = None,
        name: str | None = None,
        extra_fields: dict[str, Any] | None = None,
    ) -> None:
        """Print 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
        """
        output = self._formatter.format_result(
            action=action,
            resource=resource,
            status=status,
            identifier=identifier,
            name=name,
            extra_fields=extra_fields,
        )
        if self.format == OutputFormat.JSON:
            self._write_json_message_to_stderr(output)
            return
        self._console.file.write(f"{output}\n")
        self._console.file.flush()

    # Entity rendering
    def entity(
        self,
        entity: BaseModel,
        title: str | None,
        fields: list[FieldConfig],
        raw_payload: BaseModel | dict[str, Any] | list[Any] | None = None,
    ) -> None:
        """Render a single entity (Pydantic model).

        Args:
            entity: Pydantic model instance to render
            title: Display title for the entity
            fields: Field configurations defining what to show
        """
        if self.format == OutputFormat.JSON:
            self.raw(raw_payload if raw_payload is not None else entity)
            return
        self._entity_renderer.render(entity, title, fields)

    # List rendering
    def table(
        self,
        items: list[BaseModel],
        title: str | None,
        columns: list[ColumnConfig],
        raw_payload: BaseModel | dict[str, Any] | list[Any] | None = None,
        cursor: str | None = None,
        total_count: int | None = None,
    ) -> None:
        """Render a list of entities as a table.

        Args:
            items: List of Pydantic model instances
            title: Optional table title
            columns: Column configurations
        """
        if self.format == OutputFormat.JSON:
            self.raw(raw_payload if raw_payload is not None else items)
            return
        self._list_renderer.render(items, columns, title, cursor, total_count)

    # Raw console access for complex cases
    @property
    def console(self):
        """Get the underlying console for complex rendering needs.

        Returns:
            Rich Console instance
        """
        return self._console


# Convenience functions for direct usage (uses global singleton)
def print_success(message: str) -> None:
    """Print a success message with checkmark.

    Args:
        message: Success message to display
    """
    OutputService.get().success(message)


def print_info(message: str) -> None:
    """Print an informational message.

    Args:
        message: Info message to display
    """
    OutputService.get().info(message)


def print_warning(message: str) -> None:
    """Print a warning message.

    Args:
        message: Warning message to display
    """
    OutputService.get().warning(message)
