from __future__ import annotations

import re
from typing import List, Tuple

from rich.text import Text
from rich.style import Style

COLOR_MAP: dict[str, str] = {
    "0": "rgb(0,0,0)",
    "1": "rgb(0,0,170)",
    "2": "rgb(0,170,0)",
    "3": "rgb(0,170,170)",
    "4": "rgb(170,0,0)",
    "5": "rgb(170,0,170)",
    "6": "rgb(255,170,0)",
    "7": "rgb(170,170,170)",
    "8": "rgb(85,85,85)",
    "9": "rgb(85,85,255)",
    "a": "rgb(85,255,85)",
    "b": "rgb(85,255,255)",
    "c": "rgb(255,85,85)",
    "d": "rgb(255,85,255)",
    "e": "rgb(255,255,85)",
    "f": "rgb(255,255,255)",
    "r": "reset",
}

FORMAT_MAP: dict[str, str] = {
    "l": "bold",
    "o": "italic",
    "n": "underline",
    "m": "strike",
    "k": None,
    "r": None,
}

_ANSI_ESCAPE = re.compile(r"\x1b\[[0-9;]*m")


def strip_all(text: str) -> str:
    text = re.sub(r"§[0-9a-fk-or]", "", text, flags=re.IGNORECASE)
    text = _ANSI_ESCAPE.sub("", text)
    return text


def _parse_motd_raw(motd) -> str:
    if isinstance(motd, str):
        return motd
    if isinstance(motd, dict):
        parts: List[str] = []
        if "text" in motd:
            parts.append(motd["text"])
        if "extra" in motd and isinstance(motd["extra"], list):
            for extra in motd["extra"]:
                if isinstance(extra, dict):
                    parts.append(extra.get("text", ""))
                elif isinstance(extra, str):
                    parts.append(extra)
        return "".join(parts)
    return str(motd)


def _tokenize(text: str) -> List[Tuple[str | None, str]]:
    parts: List[Tuple[str | None, str]] = []
    regex = re.compile(r"§[0-9a-fk-or]", flags=re.IGNORECASE)
    pos = 0
    current_color: str | None = None

    for match in regex.finditer(text):
        if match.start() > pos:
            parts.append((current_color, text[pos:match.start()]))
        code = match.group().lower()
        if code == "§r":
            current_color = None
        elif code[1] in COLOR_MAP or code[1] in FORMAT_MAP:
            current_color = code
        pos = match.end()

    if pos < len(text):
        parts.append((current_color, text[pos:]))

    return parts


def to_rich(motd) -> Text:
    text = _parse_motd_raw(motd)
    tokens = _tokenize(text)
    rich_text = Text()
    current_color: str | None = None
    current_styles: List[str] = []

    for code, segment in tokens:
        if not segment:
            continue
        if code is not None:
            code_lower = code[1].lower()
            if code_lower == "r":
                current_color = None
                current_styles = []
            elif code_lower in FORMAT_MAP:
                fmt = FORMAT_MAP[code_lower]
                if fmt:
                    if fmt not in current_styles:
                        current_styles.append(fmt)
            elif code_lower in COLOR_MAP:
                current_color = COLOR_MAP[code_lower]
                current_styles = []

        if current_color or current_styles:
            style_kwargs: dict = {}
            if current_color:
                style_kwargs["color"] = current_color
            for s in current_styles:
                if s == "bold":
                    style_kwargs["bold"] = True
                elif s == "italic":
                    style_kwargs["italic"] = True
                elif s == "underline":
                    style_kwargs["underline"] = True
                elif s == "strike":
                    style_kwargs["strike"] = True
            rich_text.append(segment, Style(**style_kwargs))
        else:
            rich_text.append(segment)

    return rich_text


def to_legacy(motd) -> str:
    return _parse_motd_raw(motd)
