import re

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

acceptable_css_keywords = {
    "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 = {
    "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
