"""ASCII table formatter for dlist"""

from .table import TableFormatter
from ..helpers import chars


class ASCIIFormatter(TableFormatter):
    """ASCII table formatter with box-drawing characters

    Produces enclosed tables with ``│ ─ ┌ ┐ └ ┘ ├ ┤ ┬ ┴ ┼`` borders,
    column headers, optional multi-column layout, pagination, value
    truncation, auto-width, and categorized tree output.

    Access via ``get_formatter('ascii')`` or ``Dlist.write(format='ascii')``.

    Parameters (passed as **kwargs to ``format`` / ``format_categorized``)::

        keys      – list of column keys to display
        titles    – {key: display_name} for column headers;
                    for categories: {cat_key: format_string} e.g. 'Type: {}'
        width     – int: fixed column width (values truncated with …)
                    'auto': each column sized to its widest value
        maxwidth  – int: cap for auto-width (0 = no cap)
        nCols     – number of column groups side by side (default: 1)
        page      – rows per page; 0 = no pagination (default: 0)
        blank     – placeholder for missing values (default: ' --- ')

    **Basic table** (fixed width)::

        >>> fmt = get_formatter('ascii')
        >>> print(fmt.format(d, keys=['id', 'name', 'type'], width=10))
        ┌──────────────────────────────────┐
        │id          name        type      │
        ├──────────────────────────────────┤
        │E0001       Sintel      E         │
        │E0002       arroyito    E         │
        │E0003       oficina     A         │
        └──────────────────────────────────┘

    **Auto-width** (columns sized to content)::

        >>> print(fmt.format(d, keys=['id', 'name', 'path'], width='auto'))
        ┌──────────────────────────────────────────┐
        │id     name      path                     │
        ├──────────────────────────────────────────┤
        │E0001  Sintel    /short.mp4               │
        │E0002  arroyito  /very/long/path/to/file…│
        └──────────────────────────────────────────┘

    **Auto-width + maxwidth** (cap long columns)::

        >>> print(fmt.format(d, keys=['id', 'name', 'path'],
        ...                  width='auto', maxwidth=20))
        ┌─────────────────────────────────────┐
        │id     name      path                │
        ├─────────────────────────────────────┤
        │E0001  Sintel    /short.mp4          │
        │E0002  arroyito  /flaw/Ayacucho Cal…│
        └─────────────────────────────────────┘

    **Custom column titles**::

        >>> print(fmt.format(d, keys=['id', 'name'], width=10,
        ...                  titles={'id': 'ID', 'name': 'Name'}))
        ┌────────────────────────┐
        │ID          Name        │
        ├────────────────────────┤
        │E0001       Sintel      │
        └────────────────────────┘

    **Multi-column layout** (items flow left→right, then down)::

        >>> print(fmt.format(d, keys=['id', 'name'], nCols=3, width=10))
        ┌──────────────────────┬──────────────────────┬──────────────────────┐
        │id          name      │id          name      │id          name      │
        ├──────────────────────┼──────────────────────┼──────────────────────┤
        │E0001       Sintel    │E0002       arroyito  │E0003       oficina   │
        │E0004       test_4    │E0005       ferro     │E0006       segcam    │
        └──────────────────────┴──────────────────────┴──────────────────────┘

    **Pagination** (headers repeat every *page* rows)::

        >>> print(fmt.format(d, keys=['id', 'name'], width=10, page=3))
        ┌────────────────────────┐
        │id          name        │
        ├────────────────────────┤
        │E0001       Sintel      │
        │E0002       arroyito    │
        │E0003       oficina     │
        └────────────────────────┘
        ┌────────────────────────┐
        │id          name        │
        ├────────────────────────┤
        │E0004       test_4      │
        │E0005       ferro       │
        └────────────────────────┘

    **Categorized output** (1 level — blank-line separated)::

        >>> print(fmt.format_categorized(d, ['type'], ['id', 'name']))
        A
          ┌──────────────────────────┬─────────────...┐
          │id            name        │id            ...│
          ├──────────────────────────┼─────────────...┤
          │E0004         test_4      │ ---          ...│
          └──────────────────────────┴─────────────...┘

        E
          ┌──────────────────────────┬─────────────...┐
          │id            name        │id            ...│
          ...

    **Categorized output** (2 levels — tree graphics for subcategories)::

        >>> print(fmt.format_categorized(d, ['type', 'dir'], ['id', 'name'],
        ...       titles={'type': 'Type: {}', 'dir': '{}',
        ...               'id': 'ID', 'name': 'Name'}))
        Type: A
          ├─ /flaw/
          │ ┌──────────────────────────┬──────────...┐
          │ │ID            Name        │ID         ...│
          │ ├──────────────────────────┼──────────...┤
          │ │E0004         test_4      │ ---       ...│
          │ └──────────────────────────┴──────────...┘
          └─ /test/
            ┌──────────────────────────┬──────────...┐
            │ID            Name        │ID         ...│
            ...

        Type: E
          ├─ /
          │ ┌──────────────────────────┬──────────...┐
          ...

    **Via Dlist methods**::

        >>> d.write(format='ascii', keys=['id', 'name'], width='auto')
        >>> d.write_categorized(['type'], ['id', 'name'], format='ascii')
    """

    def _skin(self, **kwargs):
        return {
            'sep': '  ',
            'sepCol': chars.vl,
            'start': chars.vl,
            'lstart': None,
            'end': chars.vl,
            'blank': ' --- ',
            'width': 'auto',
            'nCols': 1,
            'page': 0,
        }

    def _col_widths(self, keys, fmt):
        """Get per-key widths (auto or uniform)."""
        cw = fmt.get('_col_widths')
        if cw:
            return cw
        w = fmt['width'] if isinstance(fmt['width'], int) else 1
        return {k: w for k in keys}

    def _hline(self, keys, fmt, pos='mid'):
        if not keys:
            return ''
        sep = fmt['sep']
        nCols = fmt['nCols']
        cw = self._col_widths(keys, fmt)
        # Build one group segment with per-key widths
        segment = (chars.hl * len(sep)).join(chars.hl * cw[k] for k in keys)
        # Pick corner/junction characters based on position
        if nCols <= 1:
            left  = {'top': chars.ctl, 'mid': chars.tr,  'bot': chars.cbl}
            right = {'top': chars.ctr, 'mid': chars.tl,  'bot': chars.cbr}
            return left.get(pos, chars.tr) + segment + right.get(pos, chars.tl)
        junc = {'top': chars.td, 'mid': chars.cr, 'bot': chars.tu}.get(pos, chars.cr)
        left  = {'top': chars.ctl, 'mid': chars.tr,  'bot': chars.cbl}.get(pos, chars.tr)
        right = {'top': chars.ctr, 'mid': chars.tl,  'bot': chars.cbr}.get(pos, chars.tl)
        return left + junc.join([segment] * nCols) + right

    def _header(self, keys, titles, fmt):
        if not keys:
            return ''
        sep = fmt['sep']
        sepCol = fmt['sepCol']
        start = fmt['start']
        end = fmt['end']
        nCols = fmt['nCols']
        cw = self._col_widths(keys, fmt)

        cells = [f'{{:{cw[k]}s}}'.format(titles.get(k, k)) for k in keys]
        group = sep.join(cells)
        parts = [group] * nCols
        return start + sepCol.join(parts) + end

    # ── Categorized defaults ─────────────────────────────────────────

    def _cat_skin(self, **kwargs):
        """Defaults used when called via format_categorized."""
        return {
            'nCols': 1,
            'start': chars.vl,
            'lstart': None,
            'sep': '  ',
            'sepCol': chars.vl,
            'end': chars.vl,
            'width': 'auto',
            'blank': ' --- ',
            'page': 0,
        }

    def _cat_prefix(self, label, level, depth, is_last, fmt, is_first=False):
        """Category headings: blank line + label at top level, tree at deeper."""
        if level == 0:
            return '\n' + label
        # Deeper levels: tree connectors
        indent = '  ' * level
        connector = chars.lr + chars.hl if is_last else chars.tr + chars.hl
        return f'{indent}{connector} {label}'

    def _cat_indent(self, level, total_depth, is_last, fmt):
        """Indent for table lines under a category branch."""
        if level == 0:
            return '  '
        indent = '  ' * level
        continuation = '  ' if is_last else chars.vl + ' '
        return indent + continuation

    def format_categorized(self, dlist, ctree, keys, titles={}, **kwargs):
        merged = self._cat_skin(**kwargs)
        merged.update({k: v for k, v in kwargs.items() if v is not None})
        result = super().format_categorized(dlist, ctree, keys, titles=titles,
                                            **merged)
        # Strip leading blank line from first category
        if result.startswith('\n'):
            result = result[1:]
        return result
