"""Render a list of dict rows as a GitHub-flavored Markdown table."""


def render_markdown_table(rows: list, columns: list = None, max_rows: int = 0) -> str:
    """Render a list of dict rows as a GitHub-flavored Markdown table.

    Args:
        rows: List of dictionaries, one per row. Each dictionary maps a column
            name to its cell value.
        columns: Explicit column order. When None, the keys of the first row are
            used preserving insertion order. When rows is empty and columns is
            None, the result is an empty string.
        max_rows: Maximum number of rows to render. 0 means all rows. When N > 0
            and the number of rows exceeds N, the table is truncated to N rows
            and a trailing line `\n_... N de M filas_` is appended after the
            table indicating how many of the total rows are shown.

    Returns:
        str: The Markdown table as a string. Returns an empty string when there
        are no rows and no explicit columns.
    """

    def render_cell(value) -> str:
        """Convert a cell value to its Markdown-safe string representation."""
        if value is None:
            text = ""
        elif isinstance(value, bool):
            text = "true" if value else "false"
        else:
            text = str(value)
        # Escape pipe characters so they do not break the table layout and turn
        # newlines into <br> tags to keep each row on a single Markdown line.
        text = text.replace("|", "\\|")
        text = text.replace("\r\n", "\n").replace("\r", "\n").replace("\n", "<br>")
        return text

    # Resolve the column order.
    if columns is None:
        if not rows:
            return ""
        cols = list(rows[0].keys())
    else:
        cols = list(columns)

    # Decide how many rows to render and whether truncation applies.
    total = len(rows)
    if max_rows > 0 and total > max_rows:
        visible = rows[:max_rows]
        truncated = True
    else:
        visible = rows
        truncated = False

    header = "| " + " | ".join(render_cell(col) for col in cols) + " |"
    separator = "|" + "---|" * len(cols)

    lines = [header, separator]
    for row in visible:
        cells = [render_cell(row.get(col)) for col in cols]
        lines.append("| " + " | ".join(cells) + " |")

    table = "\n".join(lines)

    if truncated:
        table += f"\n_... {max_rows} de {total} filas_"

    return table
