"""
Base conversion checker: detects 42 in various numeral systems and encodings.

Supports: binary, octal, hex, scientific notation, full-width digits,
circled digits, subscript/superscript digits, etc.
"""

import re
import unicodedata


def _try_base_prefix(s):
    """Try standard Python base prefixes: 0b, 0o, 0x, and C-style octal 052."""
    s_lower = s.lower().strip()
    try:
        if s_lower.startswith("0b"):
            val = int(s_lower, 2)
            if val == 42:
                return f"binary: {s} = 101010₂ = 42"
        elif s_lower.startswith("0o"):
            val = int(s_lower, 8)
            if val == 42:
                return f"octal: {s} = 52₈ = 42"
        elif s_lower.startswith("0x"):
            val = int(s_lower, 16)
            if val == 42:
                return f"hexadecimal: {s} = 2A₁₆ = 42"
        elif re.match(r"^0[0-7]+$", s_lower):
            # C-style octal: 052
            val = int(s_lower, 8)
            if val == 42:
                return f"octal (C-style): {s} = 52₈ = 42"
    except (ValueError, TypeError):
        pass
    return None


def _try_base_suffix(s):
    """Try common base suffixes: 101010b, 52o, 2Ah, etc."""
    s_stripped = s.strip()
    patterns = [
        # binary: 101010b or 101010B
        (r"^([01]+)[bB]$", 2, "binary"),
        # octal: 52o or 52O
        (r"^([0-7]+)[oO]$", 8, "octal"),
        # hex: 2Ah or 2AH
        (r"^([0-9a-fA-F]+)[hH]$", 16, "hexadecimal"),
    ]
    for pattern, base, name in patterns:
        m = re.match(pattern, s_stripped)
        if m:
            try:
                val = int(m.group(1), base)
                if val == 42:
                    return f"{name} (suffix notation): {s} = 42"
            except ValueError:
                pass
    return None


def _try_subscript_base(s):
    """Try subscript base notation: 101010₂, 52₈, 2A₁₆."""
    subscript_map = {"₀": "0", "₁": "1", "₂": "2", "₃": "3", "₄": "4",
                     "₅": "5", "₆": "6", "₇": "7", "₈": "8", "₉": "9"}

    s_stripped = s.strip()
    # Find trailing subscript digits
    main = []
    base_chars = []
    in_subscript = False
    for ch in s_stripped:
        if ch in subscript_map:
            in_subscript = True
            base_chars.append(subscript_map[ch])
        else:
            if in_subscript:
                # subscript chars must be at the end
                return None
            main.append(ch)

    if not base_chars or not main:
        return None

    try:
        base = int("".join(base_chars))
        if base < 2 or base > 36:
            return None
        val = int("".join(main), base)
        if val == 42:
            return f"base-{base} with subscript: {s} = 42"
    except ValueError:
        pass
    return None


def _try_scientific(s):
    """Try scientific notation: 4.2e1, 4.2E1, 4.2×10¹, etc."""
    s_stripped = s.strip()

    # Standard scientific notation
    sci_pattern = r"^[+-]?\d+\.?\d*[eE][+-]?\d+$"
    if re.match(sci_pattern, s_stripped):
        try:
            val = float(s_stripped)
            if abs(val - 42) < 1e-9:
                return f"scientific notation: {s} = 42"
        except ValueError:
            pass

    # Unicode × notation: 4.2×10¹
    superscript_map = {"⁰": "0", "¹": "1", "²": "2", "³": "3", "⁴": "4",
                       "⁵": "5", "⁶": "6", "⁷": "7", "⁸": "8", "⁹": "9",
                       "⁺": "+", "⁻": "-"}
    m = re.match(r"^([+-]?\d+\.?\d*)\s*[×xX]\s*10(.+)$", s_stripped)
    if m:
        mantissa_str, exp_part = m.group(1), m.group(2)
        # Convert superscript exponent
        exp_str = ""
        for ch in exp_part:
            if ch in superscript_map:
                exp_str += superscript_map[ch]
            elif ch.isdigit() or ch in "+-":
                exp_str += ch
            else:
                exp_str = None
                break
        if exp_str:
            try:
                val = float(mantissa_str) * (10 ** int(exp_str))
                if abs(val - 42) < 1e-9:
                    return f"scientific notation: {s} = 42"
            except (ValueError, OverflowError):
                pass

    return None


def _try_fullwidth(s):
    """Try full-width digits: ４２."""
    s_stripped = s.strip()
    if not s_stripped:
        return None

    # Full-width digit range: ０-９ (U+FF10 - U+FF19)
    converted = []
    has_fullwidth = False
    for ch in s_stripped:
        if "\uff10" <= ch <= "\uff19":
            converted.append(chr(ord(ch) - 0xff10 + ord("0")))
            has_fullwidth = True
        elif ch.isdigit():
            converted.append(ch)
        else:
            return None

    if not has_fullwidth:
        return None

    try:
        val = int("".join(converted))
        if val == 42:
            return f"full-width digits: {s} = 42"
    except ValueError:
        pass
    return None


def _try_circled_digits(s):
    """Try circled digits: ④②, ㊷ (circled 42), etc."""
    s_stripped = s.strip()
    if not s_stripped:
        return None

    # Single circled number 42: ㊷ is not standard, but let's check Unicode name
    # Circled digits ① through ⑳ (U+2460 - U+2473)
    # Then ㉑ through ㊿ (U+3251-U+325F, U+32B1-U+32BF)
    # ㊷ = U+32B7 = CIRCLED NUMBER FORTY TWO — this exists!
    if len(s_stripped) == 1:
        try:
            name = unicodedata.name(s_stripped, "")
            # e.g. "CIRCLED NUMBER FORTY TWO"
            if "FORTY" in name and "TWO" in name:
                return f"circled number: {s} (U+{ord(s_stripped):04X}) = 42"
        except (TypeError, ValueError):
            pass

    # Sequence of circled digits: ④② = 4, 2 → 42
    circled_digit_values = {}
    # ① = U+2460, value 1; ② = U+2461, value 2; ... ⑨ = U+2468, value 9
    for i in range(1, 10):
        circled_digit_values[chr(0x2460 + i - 1)] = str(i)
    # ⓪ = U+24EA, value 0
    circled_digit_values["\u24ea"] = "0"
    # ⑩ = U+2469
    circled_digit_values["\u2469"] = "10"
    # Also: ⓿ = U+24FF value 0 (negative circled)
    circled_digit_values["\u24ff"] = "0"

    # Check if all characters are single-digit circled numbers
    digits = []
    has_circled = False
    for ch in s_stripped:
        if ch in circled_digit_values:
            val = circled_digit_values[ch]
            if len(val) > 1:  # skip ⑩ etc for digit-by-digit
                return None
            digits.append(val)
            has_circled = True
        else:
            return None

    if has_circled and digits:
        try:
            val = int("".join(digits))
            if val == 42:
                return f"circled digits: {s} = 42"
        except ValueError:
            pass

    return None


def _try_unicode_numeric(s):
    """Try Unicode numeric value lookup for single characters or short strings."""
    s_stripped = s.strip()
    if not s_stripped or len(s_stripped) > 10:
        return None

    # Try interpreting each character's numeric value
    # This handles things like Devanagari digits, etc.
    digits = []
    has_special = False
    for ch in s_stripped:
        if ch.isascii() and ch.isdigit():
            digits.append(ch)
        else:
            val = unicodedata.digit(ch, -1)
            if val == -1:
                val = unicodedata.numeric(ch, -1)
            if val == -1 or val != int(val):
                return None
            digits.append(str(int(val)))
            has_special = True

    if not has_special or not digits:
        return None

    try:
        val = int("".join(digits))
        if val == 42:
            char_names = ", ".join(
                unicodedata.name(ch, f"U+{ord(ch):04X}") for ch in s_stripped
            )
            return f"Unicode digits: {s} ({char_names}) = 42"
    except ValueError:
        pass

    return None


def _try_braille(s):
    """Try braille number representation: ⠼⠙⠃ = 42.

    Braille numbers use ⠼ (number indicator) followed by:
    ⠁=1 ⠃=2 ⠉=3 ⠙=4 ⠑=5 ⠋=6 ⠛=7 ⠓=8 ⠊=9 ⠚=0
    """
    s_stripped = s.strip()
    if not s_stripped:
        return None

    # Braille digit mapping (after number indicator ⠼)
    braille_digits = {
        "\u2801": "1",  # ⠁
        "\u2803": "2",  # ⠃
        "\u2809": "3",  # ⠉
        "\u2819": "4",  # ⠙
        "\u2811": "5",  # ⠑
        "\u280b": "6",  # ⠋
        "\u281b": "7",  # ⠛
        "\u2813": "8",  # ⠓
        "\u2812": "9",  # ⠊
        "\u281a": "0",  # ⠚
    }

    # Check for number indicator ⠼ (U+283C)
    if not s_stripped.startswith("\u283c"):
        return None

    rest = s_stripped[1:]
    if not rest:
        return None

    digits = []
    for ch in rest:
        if ch in braille_digits:
            digits.append(braille_digits[ch])
        else:
            return None

    if digits:
        try:
            val = int("".join(digits))
            if val == 42:
                return f"braille number: {s} = ⠼⠙⠃ = 42"
        except ValueError:
            pass

    return None


def check(value):
    """Check if value represents 42 in a non-decimal base or encoding."""
    if not isinstance(value, str):
        return None

    s = value.strip()
    if not s:
        return None

    # Try each sub-checker
    for checker in [
        _try_base_prefix,
        _try_base_suffix,
        _try_subscript_base,
        _try_scientific,
        _try_fullwidth,
        _try_circled_digits,
        _try_unicode_numeric,
        _try_braille,
    ]:
        result = checker(s)
        if result:
            return result

    return None
