"""Excel stylesheet builder — generates styles.xml for SpreadsheetML.

Builds the complete set of fonts, fills, borders, number formats, cell
style XFs, cell XFs, cell styles, and table styles that populate the
Home > Cell Styles gallery in Excel.
"""

from __future__ import annotations

from typing import Dict, List, Tuple

from lxml import etree

from tokenmoulds.emitters.excel_constants import (
    SSML_NS,
    _BUILTIN_ACCENT1,
    _BUILTIN_ACCENT1_20,
    _BUILTIN_ACCENT1_40,
    _BUILTIN_ACCENT1_60,
    _BUILTIN_ACCENT2,
    _BUILTIN_ACCENT2_20,
    _BUILTIN_ACCENT2_40,
    _BUILTIN_ACCENT2_60,
    _BUILTIN_ACCENT3,
    _BUILTIN_ACCENT3_20,
    _BUILTIN_ACCENT3_40,
    _BUILTIN_ACCENT3_60,
    _BUILTIN_ACCENT4,
    _BUILTIN_ACCENT4_20,
    _BUILTIN_ACCENT4_40,
    _BUILTIN_ACCENT4_60,
    _BUILTIN_ACCENT5,
    _BUILTIN_ACCENT5_20,
    _BUILTIN_ACCENT5_40,
    _BUILTIN_ACCENT5_60,
    _BUILTIN_ACCENT6,
    _BUILTIN_ACCENT6_20,
    _BUILTIN_ACCENT6_40,
    _BUILTIN_ACCENT6_60,
    _BUILTIN_BAD,
    _BUILTIN_CALCULATION,
    _BUILTIN_CHECK_CELL,
    _BUILTIN_COMMA,
    _BUILTIN_COMMA_0,
    _BUILTIN_CURRENCY,
    _BUILTIN_EXPLANATORY,
    _BUILTIN_FOLLOWED_HYPERLINK,
    _BUILTIN_GOOD,
    _BUILTIN_HEADING1,
    _BUILTIN_HEADING2,
    _BUILTIN_HEADING3,
    _BUILTIN_HEADING4,
    _BUILTIN_HYPERLINK,
    _BUILTIN_INPUT,
    _BUILTIN_NEUTRAL,
    _BUILTIN_NORMAL,
    _BUILTIN_NOTE,
    _BUILTIN_OUTPUT,
    _BUILTIN_PERCENT,
    _BUILTIN_TITLE,
    _BUILTIN_TOTAL,
    _BUILTIN_WARNING,
    _CUSTOM_NUMFMT_BASE,
    _LOCALE_NUMFMTS,
    _NUMFMT_COMMA,
    _NUMFMT_COMMA_DEC,
    _NUMFMT_CURRENCY,
    _NUMFMT_GENERAL,
    _NUMFMT_PERCENT,
    _THEME_ACCENT1,
    _THEME_ACCENT2,
    _THEME_ACCENT3,
    _THEME_ACCENT4,
    _THEME_ACCENT5,
    _THEME_ACCENT6,
    _THEME_DK1,
    _THEME_DK2,
    _THEME_FOLHLINK,
    _THEME_HLINK,
    _THEME_LT1,
    _TINT_20,
    _TINT_40,
    _TINT_60,
    _fmt_size,
)
from tokenmoulds.ir import DocumentIR


def _col_index_to_letters(col_idx: int) -> str:
    """Convert a 0-based column index to Excel-style column letters."""
    result = ""
    idx = col_idx
    while True:
        result = chr(ord("A") + idx % 26) + result
        idx = idx // 26 - 1
        if idx < 0:
            break
    return result


def _sub(parent: etree._Element, tag: str, **attribs: str) -> etree._Element:
    """Create a sub-element with attributes."""
    el = etree.SubElement(parent, tag)
    for k, v in attribs.items():
        el.set(k, v)
    return el


class _StyleSheetBuilder:
    """Builds a complete SpreadsheetML styles.xml from the DocumentIR.

    Manages the cross-referenced font/fill/border/xf indices that Excel
    requires. All theme colors use <color theme="N" tint="X"/> so styles
    adapt when the user changes themes.
    """

    def __init__(self, document: DocumentIR) -> None:
        self.doc = document
        self.body = document.body_style
        self.typo = document.typography
        self.heading_styles = document.heading_styles

        # Indexed collections — order matters, index = ID
        self._fonts: List[etree._Element] = []
        self._fills: List[etree._Element] = []
        self._borders: List[etree._Element] = []
        self._numfmts: List[Tuple[str, str]] = []  # (id, formatCode)
        self._cell_style_xfs: List[etree._Element] = []  # cellStyleXfs
        self._cell_xfs: List[etree._Element] = []  # cellXfs
        self._cell_styles: List[Tuple[str, str, str]] = []  # (name, xfId, builtinId)

    def build(self) -> bytes:
        """Build the complete styles.xml."""
        self._build_fonts()
        self._build_fills()
        self._build_borders()
        self._build_numfmts()
        self._build_cell_style_xfs()
        self._build_cell_xfs()
        self._build_cell_styles()
        return self._serialize()

    # -- Fonts ----------------------------------------------------------------

    def _font(
        self,
        name: str | None = None,
        size: float | None = None,
        bold: bool = False,
        italic: bool = False,
        underline: str | None = None,
        color_theme: str | None = None,
        color_tint: str | None = None,
    ) -> int:
        """Add a font and return its index."""
        f = etree.Element("font")
        if bold:
            _sub(f, "b")
        if italic:
            _sub(f, "i")
        if underline:
            _sub(f, "u", val=underline)
        _sub(f, "sz", val=_fmt_size(size or self.body.font_size_pt))
        if color_theme is not None:
            attrs: Dict[str, str] = {"theme": color_theme}
            if color_tint:
                attrs["tint"] = color_tint
            _sub(f, "color", **attrs)
        else:
            _sub(f, "color", theme=_THEME_DK1)
        _sub(f, "name", val=name or self.body.font_family)
        _sub(
            f,
            "scheme",
            val="minor"
            if (name or self.body.font_family) == self.body.font_family
            else "major",
        )
        idx = len(self._fonts)
        self._fonts.append(f)
        return idx

    def _build_fonts(self) -> None:
        heading_font = self.typo.heading_font
        body_size = self.body.font_size_pt
        size_map = self.typo.size_map_pt

        # 0: Normal (body)
        self._font()
        # 1: Bold
        self._font(bold=True)
        # 2: Italic
        self._font(italic=True)
        # 3: Bold italic
        self._font(bold=True, italic=True)
        # 4: Title (heading font, h1 size, accent1)
        self._font(
            name=heading_font,
            size=size_map.get("h1", body_size * 2.0),
            bold=True,
            color_theme=_THEME_ACCENT1,
        )
        # 5: Heading 1 (heading font, h2 size, accent1)
        self._font(
            name=heading_font,
            size=size_map.get("h2", body_size * 1.6),
            bold=True,
            color_theme=_THEME_ACCENT1,
        )
        # 6: Heading 2 (heading font, h3 size, accent1)
        self._font(
            name=heading_font,
            size=size_map.get("h3", body_size * 1.4),
            bold=True,
            color_theme=_THEME_ACCENT1,
        )
        # 7: Heading 3 (body font, h4 size, accent1)
        self._font(
            size=size_map.get("h4", body_size * 1.2),
            bold=True,
            color_theme=_THEME_ACCENT1,
        )
        # 8: Heading 4 (body font, body size, accent1)
        self._font(bold=True, color_theme=_THEME_ACCENT1)
        # 9: Total (bold, dk1, top-border style)
        self._font(bold=True)
        # 10: Note (italic, smaller)
        self._font(italic=True, size=body_size * 0.9)
        # 11: Explanatory (italic, dk2, smaller)
        self._font(italic=True, size=body_size * 0.9, color_theme=_THEME_DK2)
        # 12: Hyperlink (underline, hlink)
        self._font(underline="single", color_theme=_THEME_HLINK)
        # 13: Followed hyperlink (underline, folHlink)
        self._font(underline="single", color_theme=_THEME_FOLHLINK)
        # 14: Good (dk2 on green-ish)
        self._font(color_theme=_THEME_DK2)
        # 15: Bad (dk2 on red-ish)
        self._font(color_theme=_THEME_DK2)
        # 16: Neutral (dk2 on yellow-ish)
        self._font(color_theme=_THEME_DK2)
        # 17: Input (dk1)
        self._font()
        # 18: Output (bold, dk2)
        self._font(bold=True, color_theme=_THEME_DK2)
        # 19: Calculation (bold, dk2)
        self._font(bold=True, color_theme=_THEME_DK2)
        # 20: Check cell (bold)
        self._font(bold=True)
        # 21: Warning (body, red-ish = accent2)
        self._font(color_theme=_THEME_ACCENT2)
        # 22: White text (for solid accent fills)
        self._font(color_theme=_THEME_LT1)

    # -- Fills ----------------------------------------------------------------

    def _fill_none(self) -> int:
        """Required empty fill at index 0."""
        f = etree.Element("fill")
        _sub(f, "patternFill")
        idx = len(self._fills)
        self._fills.append(f)
        return idx

    def _fill_gray125(self) -> int:
        """Required gray125 fill at index 1."""
        f = etree.Element("fill")
        _sub(f, "patternFill", patternType="gray125")
        idx = len(self._fills)
        self._fills.append(f)
        return idx

    def _fill_theme(self, theme: str, tint: str | None = None) -> int:
        """Solid fill from a theme color with optional tint."""
        f = etree.Element("fill")
        pf = _sub(f, "patternFill", patternType="solid")
        attrs: Dict[str, str] = {"theme": theme}
        if tint:
            attrs["tint"] = tint
        _sub(pf, "fgColor", **attrs)
        _sub(pf, "bgColor", indexed="64")
        idx = len(self._fills)
        self._fills.append(f)
        return idx

    def _build_fills(self) -> None:
        # 0,1: required fills
        self._fill_none()
        self._fill_gray125()

        # 2-25: accent fills (6 accents × 4 variants: solid, 20%, 40%, 60%)
        for accent_theme in [
            _THEME_ACCENT1,
            _THEME_ACCENT2,
            _THEME_ACCENT3,
            _THEME_ACCENT4,
            _THEME_ACCENT5,
            _THEME_ACCENT6,
        ]:
            self._fill_theme(accent_theme)  # solid
            self._fill_theme(accent_theme, _TINT_20)  # 20% (80% tint toward white)
            self._fill_theme(accent_theme, _TINT_40)  # 40%
            self._fill_theme(accent_theme, _TINT_60)  # 60%

        # 26: Good (accent6 60% tint — green-ish)
        self._fill_theme(_THEME_ACCENT3, _TINT_40)
        # 27: Bad (accent2 60% tint — red-ish)
        self._fill_theme(_THEME_ACCENT2, _TINT_40)
        # 28: Neutral (accent4 60% tint — warm)
        self._fill_theme(_THEME_ACCENT4, _TINT_40)
        # 29: Input (accent1 80% tint — very light)
        self._fill_theme(_THEME_ACCENT1, _TINT_20)
        # 30: Output (accent2 80% tint)
        self._fill_theme(_THEME_ACCENT2, _TINT_20)
        # 31: Calculation (accent4 80% tint)
        self._fill_theme(_THEME_ACCENT4, _TINT_20)
        # 32: Check cell (accent6 80% tint)
        self._fill_theme(_THEME_ACCENT3, _TINT_20)
        # 33: Note (accent4 80% tint)
        self._fill_theme(_THEME_ACCENT4, _TINT_20)

    # -- Borders --------------------------------------------------------------

    def _build_borders(self) -> None:
        # 0: empty border (required)
        b = etree.Element("border")
        for side in ["left", "right", "top", "bottom", "diagonal"]:
            _sub(b, side)
        self._borders.append(b)

        # 1: thin bottom border (for Total)
        b = etree.Element("border")
        _sub(b, "left")
        _sub(b, "right")
        top = _sub(b, "top", style="thin")
        _sub(top, "color", theme=_THEME_DK1)
        bot = _sub(b, "bottom", style="double")
        _sub(bot, "color", theme=_THEME_DK1)
        _sub(b, "diagonal")
        self._borders.append(b)

        # 2: thin all borders (for Input)
        b = etree.Element("border")
        for side in ["left", "right", "top", "bottom"]:
            s = _sub(b, side, style="thin")
            _sub(s, "color", theme=_THEME_DK1)
        _sub(b, "diagonal")
        self._borders.append(b)

        # 3: thin bottom only (for Output)
        b = etree.Element("border")
        _sub(b, "left")
        _sub(b, "right")
        _sub(b, "top")
        bot = _sub(b, "bottom", style="thin")
        _sub(bot, "color", theme=_THEME_DK1)
        _sub(b, "diagonal")
        self._borders.append(b)

    # -- Number formats -------------------------------------------------------

    def _build_numfmts(self) -> None:
        """Add custom number formats for non-US locales."""
        locale = self.doc.locale.upper() if self.doc.locale else "US"
        formats = _LOCALE_NUMFMTS.get(locale)
        if formats:
            currency_fmt, date_fmt = formats
            self._numfmts.append((str(_CUSTOM_NUMFMT_BASE), currency_fmt))
            self._numfmts.append((str(_CUSTOM_NUMFMT_BASE + 1), date_fmt))

    # -- Cell style XFs (base style definitions) ------------------------------

    def _xf(
        self,
        font_id: int = 0,
        fill_id: int = 0,
        border_id: int = 0,
        num_fmt_id: str = _NUMFMT_GENERAL,
        apply_font: bool = False,
        apply_fill: bool = False,
        apply_border: bool = False,
        apply_number: bool = False,
        apply_alignment: bool = False,
        h_align: str | None = None,
    ) -> int:
        """Add a cellStyleXf and return its index."""
        xf = etree.Element("xf")
        xf.set("numFmtId", num_fmt_id)
        xf.set("fontId", str(font_id))
        xf.set("fillId", str(fill_id))
        xf.set("borderId", str(border_id))
        if apply_font:
            xf.set("applyFont", "1")
        if apply_fill:
            xf.set("applyFill", "1")
        if apply_border:
            xf.set("applyBorder", "1")
        if apply_number:
            xf.set("applyNumberFormat", "1")
        if apply_alignment:
            xf.set("applyAlignment", "1")
            if h_align:
                _sub(xf, "alignment", horizontal=h_align)
        idx = len(self._cell_style_xfs)
        self._cell_style_xfs.append(xf)
        return idx

    def _build_cell_style_xfs(self) -> None:
        # 0: Normal
        self._xf()
        # 1: Title
        self._xf(font_id=4, apply_font=True)
        # 2: Heading 1
        self._xf(font_id=5, apply_font=True)
        # 3: Heading 2
        self._xf(font_id=6, apply_font=True)
        # 4: Heading 3
        self._xf(font_id=7, apply_font=True)
        # 5: Heading 4
        self._xf(font_id=8, apply_font=True)
        # 6: Total
        self._xf(font_id=9, border_id=1, apply_font=True, apply_border=True)
        # 7: Note
        self._xf(font_id=10, fill_id=33, apply_font=True, apply_fill=True)
        # 8: Explanatory
        self._xf(font_id=11, apply_font=True)
        # 9: Hyperlink
        self._xf(font_id=12, apply_font=True)
        # 10: Followed Hyperlink
        self._xf(font_id=13, apply_font=True)
        # 11-34: Accent fills (6 accents × 4 variants)
        for accent_offset in range(6):  # accent1-6
            base_fill = 2 + accent_offset * 4  # solid
            # Solid — white text
            self._xf(font_id=22, fill_id=base_fill, apply_font=True, apply_fill=True)
            # 20% tint — dark text
            self._xf(font_id=0, fill_id=base_fill + 1, apply_fill=True)
            # 40% tint — dark text
            self._xf(font_id=0, fill_id=base_fill + 2, apply_fill=True)
            # 60% tint — white text
            self._xf(
                font_id=22, fill_id=base_fill + 3, apply_font=True, apply_fill=True
            )
        # 35: Good
        self._xf(font_id=14, fill_id=26, apply_font=True, apply_fill=True)
        # 36: Bad
        self._xf(font_id=15, fill_id=27, apply_font=True, apply_fill=True)
        # 37: Neutral
        self._xf(font_id=16, fill_id=28, apply_font=True, apply_fill=True)
        # 38: Input
        self._xf(
            font_id=17,
            fill_id=29,
            border_id=2,
            apply_font=True,
            apply_fill=True,
            apply_border=True,
        )
        # 39: Output
        self._xf(
            font_id=18,
            fill_id=30,
            border_id=3,
            apply_font=True,
            apply_fill=True,
            apply_border=True,
        )
        # 40: Calculation
        self._xf(
            font_id=19,
            fill_id=31,
            border_id=2,
            apply_font=True,
            apply_fill=True,
            apply_border=True,
        )
        # 41: Check Cell
        self._xf(font_id=20, fill_id=32, apply_font=True, apply_fill=True)
        # 42: Warning Text
        self._xf(font_id=21, apply_font=True)
        # 43-47: Number formats
        self._xf(num_fmt_id=_NUMFMT_COMMA_DEC, apply_number=True)  # Comma
        self._xf(num_fmt_id=_NUMFMT_COMMA, apply_number=True)  # Comma [0]
        self._xf(num_fmt_id=_NUMFMT_CURRENCY, apply_number=True)  # Currency
        self._xf(num_fmt_id=_NUMFMT_PERCENT, apply_number=True)  # Percent

    def _build_cell_xfs(self) -> None:
        """Build cellXfs — one entry per cellStyleXf, referencing it by xfId."""
        for i, style_xf in enumerate(self._cell_style_xfs):
            xf = etree.Element("xf")
            xf.set("numFmtId", style_xf.get("numFmtId", "0"))
            xf.set("fontId", style_xf.get("fontId", "0"))
            xf.set("fillId", style_xf.get("fillId", "0"))
            xf.set("borderId", style_xf.get("borderId", "0"))
            xf.set("xfId", str(i))
            # Copy apply* attributes
            for attr in [
                "applyFont",
                "applyFill",
                "applyBorder",
                "applyNumberFormat",
                "applyAlignment",
            ]:
                val = style_xf.get(attr)
                if val:
                    xf.set(attr, val)
            # Copy alignment child
            align = style_xf.find("alignment")
            if align is not None:
                xf.append(align.__deepcopy__(None))  # type: ignore[arg-type]
            self._cell_xfs.append(xf)

    # -- Cell styles (named styles in ribbon) ---------------------------------

    def _build_cell_styles(self) -> None:
        self._cell_styles = [
            ("Normal", "0", _BUILTIN_NORMAL),
            ("Title", "1", _BUILTIN_TITLE),
            ("Heading 1", "2", _BUILTIN_HEADING1),
            ("Heading 2", "3", _BUILTIN_HEADING2),
            ("Heading 3", "4", _BUILTIN_HEADING3),
            ("Heading 4", "5", _BUILTIN_HEADING4),
            ("Total", "6", _BUILTIN_TOTAL),
            ("Note", "7", _BUILTIN_NOTE),
            ("Explanatory Text", "8", _BUILTIN_EXPLANATORY),
            ("Hyperlink", "9", _BUILTIN_HYPERLINK),
            ("Followed Hyperlink", "10", _BUILTIN_FOLLOWED_HYPERLINK),
        ]

        # Accent styles: 6 accents × 4 variants
        accent_names = [
            "Accent1",
            "Accent2",
            "Accent3",
            "Accent4",
            "Accent5",
            "Accent6",
        ]
        accent_builtins = [
            (
                _BUILTIN_ACCENT1,
                _BUILTIN_ACCENT1_20,
                _BUILTIN_ACCENT1_40,
                _BUILTIN_ACCENT1_60,
            ),
            (
                _BUILTIN_ACCENT2,
                _BUILTIN_ACCENT2_20,
                _BUILTIN_ACCENT2_40,
                _BUILTIN_ACCENT2_60,
            ),
            (
                _BUILTIN_ACCENT3,
                _BUILTIN_ACCENT3_20,
                _BUILTIN_ACCENT3_40,
                _BUILTIN_ACCENT3_60,
            ),
            (
                _BUILTIN_ACCENT4,
                _BUILTIN_ACCENT4_20,
                _BUILTIN_ACCENT4_40,
                _BUILTIN_ACCENT4_60,
            ),
            (
                _BUILTIN_ACCENT5,
                _BUILTIN_ACCENT5_20,
                _BUILTIN_ACCENT5_40,
                _BUILTIN_ACCENT5_60,
            ),
            (
                _BUILTIN_ACCENT6,
                _BUILTIN_ACCENT6_20,
                _BUILTIN_ACCENT6_40,
                _BUILTIN_ACCENT6_60,
            ),
        ]
        xf_base = 11  # first accent cellStyleXf
        for i, (name, builtins) in enumerate(zip(accent_names, accent_builtins)):
            xf_offset = xf_base + i * 4
            self._cell_styles.append((name, str(xf_offset), builtins[0]))
            self._cell_styles.append((f"20% - {name}", str(xf_offset + 1), builtins[1]))
            self._cell_styles.append((f"40% - {name}", str(xf_offset + 2), builtins[2]))
            self._cell_styles.append((f"60% - {name}", str(xf_offset + 3), builtins[3]))

        # Semantic styles
        self._cell_styles.extend(
            [
                ("Good", "35", _BUILTIN_GOOD),
                ("Bad", "36", _BUILTIN_BAD),
                ("Neutral", "37", _BUILTIN_NEUTRAL),
                ("Input", "38", _BUILTIN_INPUT),
                ("Output", "39", _BUILTIN_OUTPUT),
                ("Calculation", "40", _BUILTIN_CALCULATION),
                ("Check Cell", "41", _BUILTIN_CHECK_CELL),
                ("Warning Text", "42", _BUILTIN_WARNING),
            ]
        )

        # Number format styles
        self._cell_styles.extend(
            [
                ("Comma", "43", _BUILTIN_COMMA),
                ("Comma [0]", "44", _BUILTIN_COMMA_0),
                ("Currency", "45", _BUILTIN_CURRENCY),
                ("Percent", "46", _BUILTIN_PERCENT),
            ]
        )

    # -- Table styles ---------------------------------------------------------

    def _build_table_styles(self, root: etree._Element) -> None:
        """Add 6 accent table styles to the stylesheet.

        Each table style has: headerRow, firstRowStripe, secondRowStripe,
        totalRow, firstColumn, lastColumn — all using theme-linked colors.
        """
        accent_names = [
            "Accent1",
            "Accent2",
            "Accent3",
            "Accent4",
            "Accent5",
            "Accent6",
        ]
        accent_themes = [
            _THEME_ACCENT1,
            _THEME_ACCENT2,
            _THEME_ACCENT3,
            _THEME_ACCENT4,
            _THEME_ACCENT5,
            _THEME_ACCENT6,
        ]

        # We need dxfs (differential formatting) for table style elements
        dxfs_el = _sub(root, "dxfs", count=str(len(accent_themes) * 6))

        dxf_idx = 0
        table_style_entries: List[Tuple[str, List[Tuple[str, int]]]] = []

        for accent_name, theme_idx in zip(accent_names, accent_themes):
            elements: List[Tuple[str, int]] = []

            # headerRow: solid accent fill, white text, bold
            dxf = etree.SubElement(dxfs_el, "dxf")
            font = etree.SubElement(dxf, "font")
            _sub(font, "b", val="1")
            _sub(font, "color", theme=_THEME_LT1)
            fill = etree.SubElement(dxf, "fill")
            pf = _sub(fill, "patternFill", patternType="solid")
            _sub(pf, "fgColor", theme=theme_idx)
            _sub(pf, "bgColor", theme=theme_idx)
            elements.append(("headerRow", dxf_idx))
            dxf_idx += 1

            # firstRowStripe: light tint
            dxf = etree.SubElement(dxfs_el, "dxf")
            fill = etree.SubElement(dxf, "fill")
            pf = _sub(fill, "patternFill", patternType="solid")
            _sub(pf, "fgColor", theme=theme_idx, tint=_TINT_20)
            _sub(pf, "bgColor", theme=theme_idx, tint=_TINT_20)
            elements.append(("firstRowStripe", dxf_idx))
            dxf_idx += 1

            # secondRowStripe: white (no fill)
            dxf = etree.SubElement(dxfs_el, "dxf")
            elements.append(("secondRowStripe", dxf_idx))
            dxf_idx += 1

            # totalRow: bold, top border
            dxf = etree.SubElement(dxfs_el, "dxf")
            font = etree.SubElement(dxf, "font")
            _sub(font, "b", val="1")
            border = etree.SubElement(dxf, "border")
            top = _sub(border, "top", style="thin")
            _sub(top, "color", theme=theme_idx)
            elements.append(("totalRow", dxf_idx))
            dxf_idx += 1

            # firstColumn + lastColumn: bold
            for col_key in ("firstColumnStripe", "lastColumnStripe"):
                dxf = etree.SubElement(dxfs_el, "dxf")
                font = etree.SubElement(dxf, "font")
                _sub(font, "b", val="1")
                elements.append((col_key, dxf_idx))
                dxf_idx += 1

            table_style_entries.append((f"TokenMoulds {accent_name}", elements))

        # tableStyles element
        ts_el = _sub(
            root,
            "tableStyles",
            count=str(len(table_style_entries)),
            defaultTableStyle=table_style_entries[0][0],
            defaultPivotStyle="PivotStyleLight16",
        )
        for style_name, elements in table_style_entries:
            ts = _sub(
                ts_el,
                "tableStyle",
                name=style_name,
                count=str(len(elements)),
                pivot="0",
                table="1",
            )
            for element_type, dxf_id in elements:
                _sub(ts, "tableStyleElement", type=element_type, dxfId=str(dxf_id))

    # -- Serialization --------------------------------------------------------

    def _serialize(self) -> bytes:
        root = etree.Element("styleSheet", xmlns=SSML_NS)

        # numFmts (custom formats for non-US locales)
        if self._numfmts:
            nf_el = _sub(root, "numFmts", count=str(len(self._numfmts)))
            for fmt_id, fmt_code in self._numfmts:
                _sub(nf_el, "numFmt", numFmtId=fmt_id, formatCode=fmt_code)

        # fonts
        fonts_el = _sub(root, "fonts", count=str(len(self._fonts)))
        for f in self._fonts:
            fonts_el.append(f)

        # fills
        fills_el = _sub(root, "fills", count=str(len(self._fills)))
        for f in self._fills:
            fills_el.append(f)

        # borders
        borders_el = _sub(root, "borders", count=str(len(self._borders)))
        for b in self._borders:
            borders_el.append(b)

        # cellStyleXfs
        csxf_el = _sub(root, "cellStyleXfs", count=str(len(self._cell_style_xfs)))
        for xf in self._cell_style_xfs:
            csxf_el.append(xf)

        # cellXfs
        cxf_el = _sub(root, "cellXfs", count=str(len(self._cell_xfs)))
        for xf in self._cell_xfs:
            cxf_el.append(xf)

        # cellStyles
        cs_el = _sub(root, "cellStyles", count=str(len(self._cell_styles)))
        for name, xf_id, builtin_id in self._cell_styles:
            _sub(cs_el, "cellStyle", name=name, xfId=xf_id, builtinId=builtin_id)

        # dxfs + tableStyles
        self._build_table_styles(root)

        return etree.tostring(root, xml_declaration=False, encoding="unicode").encode(
            "utf-8"
        )
