import re

from cssutils import CSSParser
from cssutils.css import CSSComment, CSSMediaRule
from django.core.exceptions import ValidationError
from django.utils.translation import ugettext_lazy as _

acceptable_css_keywords = set(
    [
        'auto',
        'aqua',
        'black',
        'block',
        'blue',
        'bold',
        'both',
        'bottom',
        'brown',
        'center',
        'collapse',
        'dashed',
        'dotted',
        'fuchsia',
        'gray',
        'green',
        '!important',
        'italic',
        'left',
        'lime',
        'maroon',
        'medium',
        'none',
        'navy',
        'normal',
        'nowrap',
        'olive',
        'page',
        'pointer',
        'purple',
        'red',
        'right',
        'solid',
        'silver',
        'teal',
        'top',
        'transparent',
        'underline',
        'white',
        'yellow',
        'double',
    ]
)
valid_css_values = re.compile(
    r'^(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)$'
)
acceptable_css_properties = set(
    [
        'azimuth',
        'background-color',
        'border-bottom-color',
        'border-collapse',
        'border-color',
        'border-left-color',
        'border-right-color',
        'border-top-color',
        'break-after',
        'clear',
        'color',
        'cursor',
        'direction',
        'display',
        'elevation',
        'float',
        'font',
        'font-family',
        'font-size',
        'font-style',
        'font-variant',
        'font-weight',
        'height',
        'letter-spacing',
        'line-height',
        'max-width',
        'min-width',
        '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',
    ]
)


def validate_key(*, key, style):
    if key in acceptable_css_properties:
        return
    if key.split('-')[0] in ['background', 'border', 'margin', 'padding']:
        for value in style[key].split(' '):
            if value not in acceptable_css_keywords and not valid_css_values.match(
                value
            ):
                raise ValidationError(
                    _('"{value}" is not allowed as attribute of "{key}"').format(
                        key=key, value=value
                    )
                )
    else:
        raise ValidationError(
            _('You are not allowed to include "{key}" keys in your CSS.').format(
                key=key
            )
        )


def validate_rules(rules):
    for rule in rules:
        if isinstance(rule, CSSComment):
            continue
        if isinstance(rule, CSSMediaRule):
            validate_rules(rule.cssRules)
        else:
            style = rule.style
            for key in style.keys():
                validate_key(key=key, style=style)


def validate_css(css):
    try:
        parser = CSSParser(raiseExceptions=True)
        stylesheet = parser.parseString(css)
    except Exception as exception:
        raise ValidationError(str(exception).split('\n')[0])

    if not stylesheet.valid:
        raise ValidationError(_('This stylesheet is not valid CSS.'))

    validate_rules(stylesheet.cssRules)
    return css
