# printers/Table.py

from functools import partial
from .printers import cut_items_to_window, get_is_focused, print_bold_line, print_highlighted_line, print_line

class Table:
    @staticmethod
    def layout(min_height=5, flex=1, max_height=None):
        cfg = {"flex": flex, "min_height": min_height}
        if max_height is not None:
            cfg["max_height"] = max_height
        return cfg

    @staticmethod
    def display_state(
        screen,
        headers,
        rows,
        column_justify=None,
        column_min_widths=None,
        padding=1,
        column_delimiter=" | ",
        show_header_delimiter=True,
        header_delimiter_char="-",
        min_height=5,
        flex=1,
        max_height=None
    ):
        return {
            "headers": list(headers or []),
            "rows": list(rows or []),
            "items": list(rows or []),
            "focused": False,
            "selected_index": 0,
            "column_justify": list(column_justify or []),
            "column_min_widths": list(column_min_widths or []),
            "padding": int(padding),
            "column_delimiter": str(column_delimiter),
            "show_header_delimiter": bool(show_header_delimiter),
            "header_delimiter_char": str(header_delimiter_char),
            "layout": Table.layout(min_height, flex, max_height),
            "line_generator": partial(make_table, screen),
        }

def make_table(screen, context, remaining_height):
    num_rows, num_cols = screen.getmaxyx()
    headers = context.get("headers", [])
    rows = context.get("rows", [])
    row_attrs = list(context.get("row_attrs", []))
    is_focused = get_is_focused(context)
    selected_index = int(context.get("selected_index", 0))
    padding = max(0, int(context.get("padding", 1)))
    column_delimiter = str(context.get("column_delimiter", " | "))
    show_header_delimiter = bool(context.get("show_header_delimiter", True))
    header_delimiter_char = str(context.get("header_delimiter_char", "-"))[:1] or "-"
    column_justify = list(context.get("column_justify", []))
    column_min_widths = list(context.get("column_min_widths", []))
    headers = [str(h or "") for h in headers]
    rows = [[str(c if c is not None else "") for c in r] for r in rows]
    col_count = len(headers)
    if col_count == 0:
        return []
    if column_justify and len(column_justify) < col_count:
        column_justify = column_justify + ["left"] * (col_count - len(column_justify))
    if not column_justify:
        column_justify = ["left"] * col_count
    if column_min_widths and len(column_min_widths) < col_count:
        column_min_widths = column_min_widths + [0] * (col_count - len(column_min_widths))
    if not column_min_widths:
        column_min_widths = [0] * col_count
    widths = _compute_column_widths(headers, rows, num_cols, padding, column_delimiter, column_min_widths)
    header_line = _format_row(headers, widths, column_justify, padding, column_delimiter)
    header_line = header_line[:num_cols]
    sep_len = min(num_cols, len(header_line))
    sep_line = header_delimiter_char * sep_len
    available_for_body = max(0, remaining_height - 2)
    start, stop, visible_rows = cut_items_to_window(selected_index, rows, available_for_body)
    printers = []
    printers.append(partial(print_bold_line, 0, header_line))
    if show_header_delimiter:
        printers.append(partial(print_line, 0, sep_line))
    row_index = start
    for row in visible_rows:
        line = _format_row(row, widths, column_justify, padding, column_delimiter)
        line = line[:num_cols]
        if row_index == selected_index and is_focused:
            printers.append(partial(print_highlighted_line, 0, line))
        else:
            attr_is_bold = bool(row_attrs[row_index] if row_index < len(row_attrs) else 0)
            if attr_is_bold:
                printers.append(partial(print_bold_line, 0, line))
            else:
                printers.append(partial(print_line, 0, line))
        row_index += 1
    return printers

def _compute_column_widths(headers, rows, num_cols, padding, column_delimiter, min_widths):
    col_count = len(headers)
    content_widths = [0] * col_count
    for i in range(col_count):
        max_cell = len(headers[i])
        for r in rows:
            if i < len(r):
                cell_len = len(r[i])
                if cell_len > max_cell:
                    max_cell = cell_len
        content_widths[i] = max(max_cell, int(min_widths[i]) if i < len(min_widths) else 0, 1)
    total_padding = 2 * padding * col_count
    total_delims = len(column_delimiter) * (col_count - 1)
    total_content = sum(content_widths)
    total_line = total_content + total_padding + total_delims
    if total_line <= num_cols:
        return content_widths
    available = max(0, num_cols - total_padding - total_delims)
    min_content = sum(max(3, int(min_widths[i]) if i < len(min_widths) else 0, 1) for i in range(col_count))
    if min_content > available:
        base = available // col_count if col_count > 0 else 0
        return [max(1, base) for _ in range(col_count)]
    factor = available / float(total_content) if total_content > 0 else 1.0
    shrunk = [max(3, int(w * factor)) for w in content_widths]
    while sum(shrunk) > available:
        idx = _index_of_largest(shrunk)
        if shrunk[idx] > 3:
            shrunk[idx] -= 1
        else:
            break
    return shrunk

def _index_of_largest(values):
    best_i = 0
    best_v = values[0] if values else 0
    for i, v in enumerate(values):
        if v > best_v:
            best_i, best_v = i, v
    return best_i

def _format_row(cells, widths, justify, padding, column_delimiter):
    out = []
    for i, width in enumerate(widths):
        cell = cells[i] if i < len(cells) else ""
        just = justify[i] if i < len(justify) else "left"
        cell = _justify(_fit_text(cell, width), width, just)
        out.append((" " * padding) + cell + (" " * padding))
    return column_delimiter.join(out)

def _fit_text(text, width):
    if width <= 0:
        return ""
    if len(text) <= width:
        return text
    if width <= 1:
        return text[:width]
    if width == 2:
        return text[:2]
    return text[: width - 3] + "..."

def _justify(text, width, align):
    n = len(text)
    if n >= width:
        return text
    pad = width - n
    if align == "right":
        return (" " * pad) + text
    if align == "center":
        left = pad // 2
        right = pad - left
        return (" " * left) + text + (" " * right)
    return text + (" " * pad)
