from PIL import ImageFont

import importlib.resources as ilr


COLORS = {
    "brightgreen": "#4c1",
    "green": "#97ca00",
    "yellowgreen": "#a4a61d",
    "yellow": "#dfb317",
    "orange": "#fe7d37",
    "red": "#e05d44",
    "lightgrey": "#9f9f9f",
}


class Badge:
    """
    A small utility class for badges
    """

    def __init__(
        self,
        left_txt: str,
        right_txt: str,
        color: str,
    ):
        self.left_txt = left_txt
        self.right_txt = right_txt
        self.color = color

    def __repr__(self):
        return "[ %s | %s ]  color: %s" % (self.left_txt, self.right_txt, self.color)

    def as_svg(self) -> str:
        """Return a string containing the SVG representation of this badge"""
        return get_svg_badge(
            label_txt=self.left_txt, msg_txt=self.right_txt, color=self.color
        )


def get_svg_badge(
    label_txt: str,
    msg_txt: str,
    color: str,
    label_color: str | None = None,
) -> str:
    """
    Reads the SVG template from the package,
    fills the various information from args and returns the svg string
    """
    all_text = "%s: %s" % (label_txt, msg_txt) if label_txt else ("%s" % msg_txt)

    # Same principle as in shields.io
    template = get_local_badge_template()

    horiz_padding = 5
    vertical_margin = 0

    has_logo = False  # TODO when a logo is inserted
    total_logo_width = 0

    has_label = len(label_txt) > 0 or label_color
    label_color = label_color or "#555"
    label_margin = total_logo_width + 1

    def process_text(left_margin, content):
        """From renderText()
        https://github.com/badges/shields/blob/4415d07e8b5bf794e6675cea052cc644d0c81bb5/badge-maker/lib/badge-renderers.js#L113
        """
        text_length = preferred_width_of(content, font_size=11, font_name="Verdana")
        # todo content = escape_xml(content)
        shadow_margin = 150 + vertical_margin
        text_margin = 140 + vertical_margin
        out_text_length = 10 * text_length
        x = 10 * (left_margin + 0.5 * text_length + horiz_padding)
        return x, shadow_margin, text_margin, text_length, out_text_length

    label_x, label_shadow_margin, label_text_margin, label_width, label_text_length = (
        process_text(label_margin, content=label_txt)
    )

    left_width = (
        (label_width + 2 * horiz_padding + total_logo_width) if has_label else 0
    )

    msg_margin = left_width - (1 if len(msg_txt) > 0 else 0)
    if not has_label:
        if has_logo:
            msg_margin = msg_margin + total_logo_width + horiz_padding
        else:
            msg_margin = msg_margin + 1

    msg_x, msg_shadow_margin, msg_text_margin, msg_width, msg_text_length = (
        process_text(msg_margin, content=msg_txt)
    )

    right_width = msg_width + 2 * horiz_padding
    if has_logo and not has_label:
        right_width += total_logo_width + horiz_padding - 1

    total_width = left_width + right_width

    to_replace = {
        "title": all_text,
        "label_color": get_color(label_color),
        "color": get_color(color),
        "total_width": total_width,
        "left_width": left_width,
        "right_width": right_width,
        # label text
        "left_x": label_x,
        "left_shadow_margin": label_shadow_margin,
        "left_text_margin": label_text_margin,
        "left_out_text_length": label_text_length,
        "left_text": label_txt,
        # msg text
        "right_x": msg_x,
        "right_shadow_margin": msg_shadow_margin,
        "right_text_margin": msg_text_margin,
        "right_out_text_length": msg_text_length,
        "right_text": msg_txt,
    }
    for k, v in to_replace.items():
        template = template.replace("{{ %s }}" % k, str(v))

    return template


def get_local_badge_template() -> str:
    """Reads the SVG file template fgrom the package resources"""
    resource = ilr.files("genshield").joinpath("templates", "base.svg")
    return resource.read_text(encoding="utf-8")


def get_color(color_str: str) -> str:
    try:
        color_hexa = COLORS[color_str]
    except KeyError:
        # assume custom hexa string already
        color_hexa = color_str

    return color_hexa


def round_up_to_odd(val: int) -> int:
    return (val + 1) if (val % 2 == 0) else val


def preferred_width_of(txt: str, font_name: str, font_size: int) -> int:
    # Increase chances of pixel grid alignment.
    font_file = "fonts/%s.ttf" % font_name.lower()
    try:
        # Try from name only - this works if the font is known by the OS
        font = ImageFont.truetype(font=font_file, size=font_size)
    except OSError:
        # Font not found: use the embedded font file from the package
        resource = ilr.files("genshield").joinpath(font_file)
        try:
            # Prefer a real filesystem path if available
            with ilr.as_file(resource) as font_path:
                font = ImageFont.truetype(font=str(font_path), size=font_size)
        except FileNotFoundError:
            # As a fallback, try by font name (e.g., "Verdana")
            font = ImageFont.truetype(font=font_name, size=font_size)

    # PLI.FreeTypeFont does not have a getsize() method, however, the FreeTypeFont class is not part of PLI's API.
    # Thus, we can not use isinstance(font, FreeTypeFont) here.
    getsize = getattr(font, "getsize", None)
    if callable(getsize):
        width = font.getsize(txt)[0]
    else:
        width = font.getbbox(txt)[2]  # exists for FreeTypeFont in PLI >= v10.0.0
    return round_up_to_odd(width)
