from typing import Any, get_args, get_origin

from pydantic import BaseModel
from pydantic.fields import FieldInfo
from rich.console import Console
from rich.table import Table


def format_field_type(annotation: Any) -> str:
    """Форматирует тип поля для отображения"""
    if annotation is None:
        return "None"

    origin = get_origin(annotation)

    if origin is not None:
        args = get_args(annotation)
        origin_name = getattr(origin, "__name__", str(origin))

        if origin_name == "UnionType" or str(origin) == "typing.Union":
            args_str = " | ".join(format_field_type(a) for a in args)
            return args_str

        if args:
            args_str = ", ".join(format_field_type(a) for a in args)
            return f"{origin_name}[{args_str}]"
        return origin_name

    if isinstance(annotation, type) and issubclass(annotation, BaseModel):
        return f"[magenta]{annotation.__name__}[/magenta]"

    return getattr(annotation, "__name__", str(annotation))


def print_model_fields(
    console: Console,
    model_class: type[BaseModel],
    indent: int = 0,
    visited: set | None = None,
) -> None:
    """Рекурсивно выводит поля модели с поддержкой вложенных моделей"""
    if visited is None:
        visited = set()

    if model_class in visited:
        console.print(
            f"{'  ' * indent}[dim](циклическая ссылка на {model_class.__name__})[/dim]"
        )
        return
    visited.add(model_class)

    prefix = "  " * indent

    for field_name, field_info in model_class.model_fields.items():
        annotation = field_info.annotation
        field_type = format_field_type(annotation)
        default = field_info.default
        description = field_info.description or ""

        if default is None:
            default_str = "[dim]None[/dim]"
        elif default is ...:
            default_str = "[red]required[/red]"
        elif isinstance(default, bool):
            default_str = "[green]true[/green]" if default else "[red]false[/red]"
        else:
            default_str = str(default)

        console.print(
            f"{prefix}[yellow]{field_name}[/yellow]: {field_type} = {default_str}"
            + (f"  [dim]# {description}[/dim]" if description else "")
        )

        nested_model = None

        if isinstance(annotation, type) and issubclass(annotation, BaseModel):
            nested_model = annotation
        else:
            origin = get_origin(annotation)
            if origin is not None:
                for arg in get_args(annotation):
                    if isinstance(arg, type) and issubclass(arg, BaseModel):
                        nested_model = arg
                        break

        if nested_model is not None:
            console.print(f"{prefix}  [dim]└─ {nested_model.__name__}:[/dim]")
            print_model_fields(console, nested_model, indent + 2, visited.copy())


def print_model_table(
    console: Console,
    model_class: type[BaseModel],
    prefix: str = "",
    visited: set | None = None,
) -> Table:
    """Выводит поля модели в виде таблицы с вложенными моделями"""
    if visited is None:
        visited = set()

    table = Table(show_header=True, box=None if prefix else None)
    table.add_column("Поле", style="yellow")
    table.add_column("Тип", style="cyan")
    table.add_column("По умолчанию")
    table.add_column("Описание", style="dim")

    _add_model_rows(table, model_class, prefix, visited)

    return table


def _add_model_rows(
    table: Table,
    model_class: type[BaseModel],
    prefix: str = "",
    visited: set | None = None,
) -> None:
    """Добавляет строки модели в таблицу рекурсивно"""
    if visited is None:
        visited = set()

    if model_class in visited:
        return
    visited.add(model_class)

    for field_name, field_info in model_class.model_fields.items():
        annotation = field_info.annotation
        field_type = format_field_type(annotation)
        default = field_info.default
        description = field_info.description or ""

        if default is None:
            default_str = "-"
        elif default is ...:
            default_str = "[red]required[/red]"
        elif isinstance(default, bool):
            default_str = "true" if default else "false"
        elif isinstance(default, BaseModel):
            default_str = "{...}"
        else:
            default_str = str(default)[:20]

        full_name = f"{prefix}{field_name}" if prefix else field_name
        table.add_row(full_name, field_type, default_str, description[:40])

        nested_model = None

        if isinstance(annotation, type) and issubclass(annotation, BaseModel):
            nested_model = annotation
        else:
            origin = get_origin(annotation)
            if origin is not None:
                for arg in get_args(annotation):
                    if isinstance(arg, type) and issubclass(arg, BaseModel):
                        nested_model = arg
                        break

        if nested_model is not None and nested_model not in visited:
            _add_model_rows(table, nested_model, f"  {full_name}.", visited.copy())
