import logging
from typing import Generator, List, Optional, Union


log = logging.getLogger(__name__)


BASE36_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"


def expand_range(range_input: Union[str, List[str]]) -> List[str]:
    """expand range to member list; input: one item, range, list of either"""
    if not range_input:
        return []

    # normalize to list
    items = range_input.split(",") if isinstance(range_input, str) else range_input

    if not isinstance(items, list):
        log.warning(f"range must be string or list, got {type(range_input)}")
        return []

    expanded = []
    for item in items:
        if not isinstance(item, str):
            log.warning(f"range must be string, got {type(item)}")
            continue

        item = item.strip()

        try:
            if ":" in item and item.count(":") == 1:
                start, end = item.split(":")
                expanded.extend(sequence(start, end))
            else:
                expanded.append(item)
        except Exception as e:
            log.warning(f"error expanding member {item}: {e}")
            expanded.append(item)  # fallback to original

    return expanded


def increment(value: Union[str, int, None] = None) -> str:
    """increment base36 string by 1"""
    normalized = to_string(value)
    if not normalized:
        return "0"

    chars = list(normalized)
    carry = True

    for i in range(len(chars) - 1, -1, -1):
        if not carry:
            break

        if chars[i] == "9":
            chars[i] = "A"
            carry = False
        elif chars[i] == "Z":
            chars[i] = "0"
        else:
            chars[i] = chr(ord(chars[i]) + 1)
            carry = False

    if carry:
        chars.insert(0, "1")

    return "".join(chars)


def sequence(
    start: Union[str, int, None] = None, end: Union[str, int, None] = None
) -> Generator[str, None, None]:
    """generate base36 sequence from start to end (inclusive)"""
    start_str = to_string(start)
    end_str = to_string(end)

    if not start_str or not end_str:
        log.warning("both start and end required for sequence")
        return

    start_int = to_int(start_str)
    end_int = to_int(end_str)

    if start_int > end_int:
        return

    for i in range(start_int, end_int + 1):
        yield from_int(i).zfill(3)


def to_int(base36: str) -> int:
    """convert base36 string to integer"""
    return int(base36, 36)


def from_int(num: int) -> str:
    """convert integer to base36 string"""
    if num == 0:
        return "0"

    result = ""
    while num > 0:
        result = BASE36_CHARS[num % 36] + result
        num //= 36

    return result


def to_string(value: Union[str, int, None]) -> Optional[str]:
    """normalize input to string or return None if invalid"""
    if not value and value != 0:
        return None

    if isinstance(value, int):
        return str(value)
    elif isinstance(value, str):
        return value
    else:
        log.warning(f"value must be string or integer, got {type(value)}")
        return None
