import io
import re
from dataclasses import dataclass
from enum import Enum, auto
from typing import Generator, Iterable


class NumberClass(Enum):
    ROMAN_NUMERAL = auto()  # 罗马数字
    LOWERCASE_ROMAN_NUMERAL = auto()  # 小写罗马数字
    CIRCLED_NUMBER = auto()  # 带圆圈的数字
    PARENTHESIZED_CHINESE = auto()  # 括号中的汉字
    CIRCLED_CHINESE = auto()  # 带圆圈的汉字
    BLACK_CIRCLED_NUMBER = auto()  # 黑色圆圈数字
    UNBOUNDED_NUMBER = auto()  # 无包围数字


class NumberStyle(Enum):
    ROMAN_NUMERAL = auto()  # 罗马数字
    LOWERCASE_ROMAN_NUMERAL = auto()  # 小写罗马数字
    CIRCLED_NUMBER = auto()  # 带圆圈的数字
    DOUBLE_CIRCLED_NUMBER = auto()  # 双圈数字
    CIRCLED_SANS_SERIF_NUMBER = auto()  # 带圆圈的无衬线数字
    BLACK_CIRCLED_SANS_SERIF_NUMBER = auto()  # 黑色圆圈无衬线数字
    BLACK_CIRCLED_NUMBER = auto()  # 黑色圆圈数字
    PARENTHESIZED_CHINESE = auto()  # 括号中的汉字
    CIRCLED_CHINESE = auto()  # 带圆圈的汉字
    FULL_WIDTH_NUMBER = auto()  # 全角数字
    MATHEMATICAL_BOLD_NUMBER = auto()  # 数学粗体数字
    ARTISTIC_BOLD_NUMBER = auto()  # 艺术粗体数字
    OUTLINED_BOLD_NUMBER = auto()  # 描边的粗体数字
    SUBSCRIPT_NUMBER = auto()  # 带角标的数字


@dataclass
class Mark:
    number: int
    char: str
    clazz: NumberClass
    style: NumberStyle

    def __str__(self) -> str:
        return self.char

    def __hash__(self):
        return hash((self.clazz, self.number))

    def __eq__(self, other) -> bool:
        if not isinstance(other, Mark):
            return False
        if self.clazz != other.clazz:
            return False
        if self.number != other.number:
            return False
        return True


def samples(number_style: NumberStyle, count: int) -> str:
    if count <= 1:
        raise ValueError("Count must be greater than 1")
    half_count = count // 2
    number_styles = _number_marks.styles.get(number_style, None)
    if number_styles is None:
        raise ValueError(f"Invalid number style: {number_style.name}")
    buffer = io.StringIO()
    for char in number_styles[:half_count]:
        buffer.write(char)
    buffer.write("...")
    for char in number_styles[-half_count:]:
        buffer.write(char)
    return buffer.getvalue()


def transform2mark(raw_char: str) -> Mark | None:
    gotten = _number_marks.marks.get(raw_char, None)
    if gotten is None:
        return None
    return Mark(
        number=gotten.number, char=gotten.char, clazz=gotten.clazz, style=gotten.style
    )


def search_marks(text: str) -> Generator[Mark | str, None, None]:
    for part in re.split(_number_marks.pattern, text):
        mark = transform2mark(part)
        if mark is None:
            yield part
        else:
            yield mark


class _NumberMarks:
    def __init__(
        self,
        styles: Iterable[tuple[NumberClass, NumberStyle, Iterable[tuple[int, str]]]],
    ):
        self.marks: dict[str, Mark] = {}
        self.styles: dict[NumberStyle, list[str]] = {}
        for clazz, style, marks in styles:
            for number, mark in marks:
                self.marks[mark] = Mark(number, mark, clazz, style)
                self.styles[style] = [
                    char for _, char in sorted(marks, key=lambda x: x[0])
                ]

        self.pattern: re.Pattern = re.compile(
            r"([" + "".join(sorted(list(self.marks.keys()))) + r"])"
        )


# some of they are from https://tw.piliapp.com/symbol/number/
_number_marks = _NumberMarks(
    (
        (
            NumberClass.ROMAN_NUMERAL,
            NumberStyle.ROMAN_NUMERAL,
            (
                (1, "Ⅰ"),
                (2, "Ⅱ"),
                (3, "Ⅲ"),
                (4, "Ⅳ"),
                (5, "Ⅴ"),
                (6, "Ⅵ"),
                (7, "Ⅶ"),
                (8, "Ⅷ"),
                (9, "Ⅸ"),
                (10, "Ⅹ"),
                (11, "Ⅺ"),
                (12, "Ⅻ"),
            ),
        ),
        (
            NumberClass.LOWERCASE_ROMAN_NUMERAL,
            NumberStyle.LOWERCASE_ROMAN_NUMERAL,
            (
                (1, "ⅰ"),
                (2, "ⅱ"),
                (3, "ⅲ"),
                (4, "ⅳ"),
                (5, "ⅴ"),
                (6, "ⅵ"),
                (7, "ⅶ"),
                (8, "ⅷ"),
                (9, "ⅸ"),
                (10, "ⅹ"),
                (11, "ⅺ"),
                (12, "ⅻ"),
            ),
        ),
        (
            NumberClass.CIRCLED_NUMBER,
            NumberStyle.CIRCLED_NUMBER,
            (
                (0, "⓪"),
                (1, "①"),
                (2, "②"),
                (3, "③"),
                (4, "④"),
                (5, "⑤"),
                (6, "⑥"),
                (7, "⑦"),
                (8, "⑧"),
                (9, "⑨"),
                (10, "⑩"),
                (11, "⑪"),
                (12, "⑫"),
                (13, "⑬"),
                (14, "⑭"),
                (15, "⑮"),
                (16, "⑯"),
                (17, "⑰"),
                (18, "⑱"),
                (19, "⑲"),
                (20, "⑳"),
                (21, "㉑"),
                (22, "㉒"),
                (23, "㉓"),
                (24, "㉔"),
                (25, "㉕"),
                (26, "㉖"),
                (27, "㉗"),
                (28, "㉘"),
                (29, "㉙"),
                (30, "㉚"),
                (31, "㉛"),
                (32, "㉜"),
                (33, "㉝"),
                (34, "㉞"),
                (35, "㉟"),
                (36, "㊱"),
                (37, "㊲"),
                (38, "㊳"),
                (39, "㊴"),
                (40, "㊵"),
                (41, "㊶"),
                (42, "㊷"),
                (43, "㊸"),
                (44, "㊹"),
                (45, "㊺"),
                (46, "㊻"),
                (47, "㊼"),
                (48, "㊽"),
                (49, "㊾"),
                (50, "㊿"),
            ),
        ),
        (
            NumberClass.CIRCLED_NUMBER,
            NumberStyle.DOUBLE_CIRCLED_NUMBER,
            (
                (0, "⓵"),
                (1, "⓶"),
                (2, "⓷"),
                (3, "⓸"),
                (4, "⓹"),
                (5, "⓺"),
                (6, "⓻"),
                (7, "⓼"),
                (8, "⓽"),
                (9, "⓾"),
            ),
        ),
        (
            NumberClass.CIRCLED_NUMBER,
            NumberStyle.CIRCLED_SANS_SERIF_NUMBER,
            (
                (1, "➀"),
                (2, "➁"),
                (3, "➂"),
                (4, "➃"),
                (5, "➄"),
                (6, "➅"),
                (7, "➆"),
                (8, "➇"),
                (9, "➈"),
                (10, "➉"),
            ),
        ),
        (
            NumberClass.BLACK_CIRCLED_NUMBER,
            NumberStyle.BLACK_CIRCLED_SANS_SERIF_NUMBER,
            (
                (1, "➊"),
                (2, "➋"),
                (3, "➌"),
                (4, "➍"),
                (5, "➎"),
                (6, "➏"),
                (7, "➐"),
                (8, "➑"),
                (9, "➒"),
                (10, "➓"),
            ),
        ),
        (
            NumberClass.BLACK_CIRCLED_NUMBER,
            NumberStyle.BLACK_CIRCLED_NUMBER,
            (
                (0, "⓿"),
                (1, "❶"),
                (2, "❷"),
                (3, "❸"),
                (4, "❹"),
                (5, "❺"),
                (6, "❻"),
                (7, "❼"),
                (8, "❽"),
                (9, "❾"),
                (10, "❿"),
                (11, "⓫"),
                (12, "⓬"),
                (13, "⓭"),
                (14, "⓮"),
                (15, "⓯"),
                (16, "⓰"),
                (17, "⓱"),
                (18, "⓲"),
                (19, "⓳"),
                (20, "⓴"),
            ),
        ),
        (
            NumberClass.PARENTHESIZED_CHINESE,
            NumberStyle.PARENTHESIZED_CHINESE,
            (
                (1, "㈠"),
                (2, "㈡"),
                (3, "㈢"),
                (4, "㈣"),
                (5, "㈤"),
                (6, "㈥"),
                (7, "㈦"),
                (8, "㈧"),
                (9, "㈨"),
                (10, "㈩"),
            ),
        ),
        (
            NumberClass.CIRCLED_CHINESE,
            NumberStyle.CIRCLED_CHINESE,
            (
                (1, "㊀"),
                (2, "㊁"),
                (3, "㊂"),
                (4, "㊃"),
                (5, "㊄"),
                (6, "㊅"),
                (7, "㊆"),
                (8, "㊇"),
                (9, "㊈"),
                (10, "㊉"),
            ),
        ),
        (
            NumberClass.UNBOUNDED_NUMBER,
            NumberStyle.FULL_WIDTH_NUMBER,
            (
                (0, "０"),
                (1, "１"),
                (2, "２"),
                (3, "３"),
                (4, "４"),
                (5, "５"),
                (6, "６"),
                (7, "７"),
                (8, "８"),
                (9, "９"),
            ),
        ),
        (
            NumberClass.UNBOUNDED_NUMBER,
            NumberStyle.MATHEMATICAL_BOLD_NUMBER,
            (
                (0, "𝟬"),
                (1, "𝟭"),
                (2, "𝟮"),
                (3, "𝟯"),
                (4, "𝟰"),
                (5, "𝟱"),
                (6, "𝟲"),
                (7, "𝟳"),
                (8, "𝟴"),
                (9, "𝟵"),
            ),
        ),
        (
            NumberClass.UNBOUNDED_NUMBER,
            NumberStyle.ARTISTIC_BOLD_NUMBER,
            (
                (0, "𝟎"),
                (1, "𝟏"),
                (2, "𝟐"),
                (3, "𝟑"),
                (4, "𝟒"),
                (5, "𝟓"),
                (6, "𝟔"),
                (7, "𝟕"),
                (8, "𝟖"),
                (9, "𝟗"),
            ),
        ),
        (
            NumberClass.UNBOUNDED_NUMBER,
            NumberStyle.OUTLINED_BOLD_NUMBER,
            (
                (0, "𝟘"),
                (1, "𝟙"),
                (2, "𝟚"),
                (3, "𝟛"),
                (4, "𝟜"),
                (5, "𝟝"),
                (6, "𝟞"),
                (7, "𝟟"),
                (8, "𝟠"),
                (9, "𝟡"),
            ),
        ),
        (
            NumberClass.UNBOUNDED_NUMBER,
            NumberStyle.SUBSCRIPT_NUMBER,
            (
                (0, "🄁"),
                (1, "🄂"),
                (2, "🄃"),
                (3, "🄄"),
                (4, "🄅"),
                (5, "🄆"),
                (6, "🄇"),
                (7, "🄈"),
                (8, "🄉"),
                (9, "🄊"),
            ),
        ),
    )
)
