"""Functions to sanitize user input files."""

import nh3

# Allowed CSS properties for SVG sanitization (combines general CSS and SVG-specific properties)
_SVG_ALLOWED_CSS_PROPERTIES = frozenset([
    # General CSS (matching bleach's original ALLOWED_CSS_PROPERTIES)
    'azimuth',
    'background-color',
    'border-bottom-color',
    'border-collapse',
    'border-color',
    'border-left-color',
    'border-right-color',
    'border-top-color',
    'clear',
    'color',
    'cursor',
    'direction',
    'display',
    'elevation',
    'float',
    'font',
    'font-family',
    'font-size',
    'font-style',
    'font-variant',
    'font-weight',
    'height',
    'letter-spacing',
    'line-height',
    'overflow',
    'pause',
    'pause-after',
    'pause-before',
    'pitch',
    'pitch-range',
    'richness',
    'speak',
    'speak-header',
    'speak-numeral',
    'speak-punctuation',
    'speech-rate',
    'stress',
    'text-align',
    'text-decoration',
    'text-indent',
    'unicode-bidi',
    'vertical-align',
    'voice-family',
    'volume',
    'white-space',
    'width',
    # SVG-specific CSS (matching bleach's ALLOWED_SVG_PROPERTIES)
    'fill',
    'fill-opacity',
    'fill-rule',
    'stroke',
    'stroke-linecap',
    'stroke-linejoin',
    'stroke-opacity',
    'stroke-width',
])

ALLOWED_ELEMENTS_SVG = [
    'a',
    'animate',
    'animateColor',
    'animateMotion',
    'animateTransform',
    'circle',
    'defs',
    'desc',
    'ellipse',
    'font-face',
    'font-face-name',
    'font-face-src',
    'g',
    'glyph',
    'hkern',
    'linearGradient',
    'line',
    'marker',
    'metadata',
    'missing-glyph',
    'mpath',
    'path',
    'polygon',
    'polyline',
    'radialGradient',
    'rect',
    'set',
    'stop',
    'svg',
    'switch',
    'text',
    'title',
    'tspan',
    'use',
]

ALLOWED_ATTRIBUTES_SVG = [
    'accent-height',
    'accumulate',
    'additive',
    'alphabetic',
    'arabic-form',
    'ascent',
    'attributeName',
    'attributeType',
    'baseProfile',
    'bbox',
    'begin',
    'by',
    'calcMode',
    'cap-height',
    'class',
    'color',
    'color-rendering',
    'content',
    'cx',
    'cy',
    'd',
    'dx',
    'dy',
    'descent',
    'display',
    'dur',
    'end',
    'fill',
    'fill-opacity',
    'fill-rule',
    'font-family',
    'font-size',
    'font-stretch',
    'font-style',
    'font-variant',
    'font-weight',
    'from',
    'fx',
    'fy',
    'g1',
    'g2',
    'glyph-name',
    'gradientUnits',
    'hanging',
    'height',
    'horiz-adv-x',
    'horiz-origin-x',
    'id',
    'ideographic',
    'k',
    'keyPoints',
    'keySplines',
    'keyTimes',
    'lang',
    'marker-end',
    'marker-mid',
    'marker-start',
    'markerHeight',
    'markerUnits',
    'markerWidth',
    'mathematical',
    'max',
    'min',
    'name',
    'offset',
    'opacity',
    'orient',
    'origin',
    'overline-position',
    'overline-thickness',
    'panose-1',
    'path',
    'pathLength',
    'points',
    'preserveAspectRatio',
    'r',
    'refX',
    'refY',
    'repeatCount',
    'repeatDur',
    'requiredExtensions',
    'requiredFeatures',
    'restart',
    'rotate',
    'rx',
    'ry',
    'slope',
    'stemh',
    'stemv',
    'stop-color',
    'stop-opacity',
    'strikethrough-position',
    'strikethrough-thickness',
    'stroke',
    'stroke-dasharray',
    'stroke-dashoffset',
    'stroke-linecap',
    'stroke-linejoin',
    'stroke-miterlimit',
    'stroke-opacity',
    'stroke-width',
    'systemLanguage',
    'target',
    'text-anchor',
    'to',
    'transform',
    'type',
    'u1',
    'u2',
    'underline-position',
    'underline-thickness',
    'unicode',
    'unicode-range',
    'units-per-em',
    'values',
    'version',
    'viewBox',
    'visibility',
    'width',
    'widths',
    'x',
    'x-height',
    'x1',
    'x2',
    'xlink:actuate',
    'xlink:arcrole',
    'xlink:href',
    'xlink:role',
    'xlink:show',
    'xlink:title',
    'xlink:type',
    'xml:base',
    'xml:lang',
    'xml:space',
    'xmlns',
    'xmlns:xlink',
    'y',
    'y1',
    'y2',
    'zoomAndPan',
    'style',
]

# Default allowlists (matching bleach's original defaults)
# TODO: I do not see us needing a bunch of these but I do not want to introduce a breaking change; we might want to narroy this down with the next breaking change
DEFAULT_TAGS = frozenset([
    'a',
    'abbr',
    'acronym',
    'b',
    'blockquote',
    'code',
    'em',
    'i',
    'li',
    'ol',
    'strong',
    'ul',
])
DEAFAULT_ATTRS = {'a': {'href', 'title'}, 'abbr': {'title'}, 'acronym': {'title'}}
DEFAULT_CSS = frozenset([
    'azimuth',
    'background-color',
    'border-bottom-color',
    'border-collapse',
    'border-color',
    'border-left-color',
    'border-right-color',
    'border-top-color',
    'clear',
    'color',
    'cursor',
    'direction',
    'display',
    'elevation',
    'float',
    'font',
    'font-family',
    'font-size',
    'font-style',
    'font-variant',
    'font-weight',
    'height',
    'letter-spacing',
    'line-height',
    'overflow',
    'pause',
    'pause-after',
    'pause-before',
    'pitch',
    'pitch-range',
    'richness',
    'speak',
    'speak-header',
    'speak-numeral',
    'speak-punctuation',
    'speech-rate',
    'stress',
    'text-align',
    'text-decoration',
    'text-indent',
    'unicode-bidi',
    'vertical-align',
    'voice-family',
    'volume',
    'white-space',
    'width',
])
# TODO: We might want to respect the setting EXTRA_URL_SCHEMES here but that would be breaking
DEFAULT_PROTOCOLS = frozenset(['http', 'https', 'mailto'])


def sanitize_svg(
    file_data,
    strip: bool = True,
    elements: list[str] = ALLOWED_ELEMENTS_SVG,
    attributes: list[str] = ALLOWED_ATTRIBUTES_SVG,
) -> str:
    """Sanitize a SVG file.

    Args:
        file_data (str): SVG as string.
        strip (bool, optional): Should invalid elements get removed. Defaults to True.
        elements (str, optional): Allowed elements. Defaults to ALLOWED_ELEMENTS_SVG.
        attributes (str, optional): Allowed attributes. Defaults to ALLOWED_ATTRIBUTES_SVG.

    Returns:
        str: Sanitzied SVG file.
    """
    # Handle byte-encoded data
    if isinstance(file_data, bytes):
        file_data = file_data.decode('utf-8')

    # nh3 requires attributes as dict[str, set[str]]; convert from list (allowed for all elements)
    attrs_dict = {elem: set(attributes) for elem in elements}

    cleaned = nh3.clean(
        file_data,
        tags=set(elements),
        attributes=attrs_dict,
        filter_style_properties=_SVG_ALLOWED_CSS_PROPERTIES,
        strip_comments=strip,
        link_rel=None,
    )

    # Replace non-breaking spaces with regular spaces to prevent SVG rendering issues
    for nbsp in ['&nbsp;', '&#160;']:
        cleaned = cleaned.replace(nbsp, ' ')

    return cleaned
