from dataclasses import dataclass, field
from math import ceil
from typing import Union

# Basic GSM-7 char set (without extension table handling).
# If you want 100% accuracy for GSM-7, we can add the extension table later.
GSM7_BASIC = set(
    "@£$¥èéùìòÇ\nØø\rÅåΔ_ΦΓΛΩΠΨΣΘΞ"
    "ÆæßÉ !\"#¤%&'()*+,-./"
    "0123456789:;<=>?"
    "¡ABCDEFGHIJKLMNOPQRSTUVWXYZÄÖÑÜ§"
    "¿abcdefghijklmnopqrstuvwxyzäöñüà"
)


def is_gsm7(text: str) -> bool:
    return all(ch in GSM7_BASIC for ch in text)


def ucs2_units(text: str) -> int:
    # UTF-16 code units: emoji counts as 2 (surrogate pair), which matches UCS-2 segment math used in practice.
    return len(text.encode("utf-16-be")) // 2


def fix_surrogates(text: str) -> str:
    # Turns surrogate pairs like \uD83D\uDC96 into the real character (💖)
    return text.encode("utf-16", "surrogatepass").decode("utf-16")


@dataclass
class MessageSmartEncoding:
    """
    Sms Message Representation
    """

    replacements = {
        "“": '"',
        "”": '"',
        "‘": "'",
        "’": "'",
        "–": "-",
        "—": "-",
        "…": "...",
        "•": "-",
        "€": "EUR",
        "™": "",
        "©": "",
        "®": "",
        "\u0002": "",
        "\u001b": "",
    }
    body: Union[str, list]
    normalized_text: str = field(init=False)
    length: int = field(init=False)
    segments: int = field(init=False)

    def __post_init__(self):
        body_text = (
            " ".join(map(str, self.body))
            if isinstance(self.body, list)
            else str(self.body)
        )
        self.normalized_text = fix_surrogates(self.normalize(body_text))

        if is_gsm7(self.normalized_text):
            self.encoding = "GSM-7"
            text_length = len(self.normalized_text)
            self.length = text_length
            per_segment = 160 if text_length <= 160 else 153
            self.segments = ceil(text_length / per_segment)
        else:
            self.encoding = "UCS-2"
            text_units_count = ucs2_units(self.normalized_text)
            self.length = text_units_count
            per_segment = 70 if text_units_count <= 70 else 67
            self.segments = ceil(text_units_count / per_segment)

    def normalize(self, text: str) -> str:
        for char, replacement in self.replacements.items():
            text = text.replace(char, replacement)
        return text
