
from .constants import NUMBER_WORDS, MAGNITUDE_MAP, HUNDRED_PREFIXES, ORDINAL_MAP

def num_to_words(num):
    """
    Converts a number (int, float, or string representation) into its Bangla spoken form.
    Handles decimals, integer strings, and large denominations (Crore, Lakh).
    """
    s_num = str(num).strip()
    
    # CASE 1: Decimals
    if '.' in s_num:
        whole, frac = s_num.split('.', 1)
        whole_text = num_to_words(whole)
        
        # Convert fractional part digit by digit
        frac_text_list = []
        for digit in frac:
            # Safe get for digit word, fallback to digit itself
            word = NUMBER_WORDS.get(digit, [digit])[0]
            frac_text_list.append(word)
        
        frac_text = " ".join(frac_text_list)
        return f"{whole_text} দশমিক {frac_text}"

    # CASE 2: Clean Zero Handling
    # Strip leading zeros, but keep a single '0' if the string becomes empty
    s_num = s_num.lstrip('0')
    if not s_num:
        return NUMBER_WORDS['0'][0]

    length = len(s_num)
    parts = []

    # CASE 3: Very Large Numbers (> 9 digits)
    # The current logic handles standard Bengali denomination up to Crores.
    # For strings larger than 9 digits, we treat it as a sequence of digits 
    # (often phone numbers or overly large identifiers).
    if length > 9:
        for digit in s_num:
            parts.append(NUMBER_WORDS.get(digit, [digit])[0])
        return " ".join(parts)

    # Recursive decomposition helper
    def decompose(remaining_str, n_len):
        if n_len == 0:
            return

        # Crores (1,00,00,000 range) - 8th & 9th digits
        if n_len >= 8:
            # Extract anything above 7 digits
            split_idx = n_len - 7
            head = remaining_str[:split_idx]
            tail = remaining_str[split_idx:]
            
            # Recurse on the magnitude
            parts.append(num_to_words(head))
            parts.append(MAGNITUDE_MAP[7][0])
            
            # Continue with rest
            decompose(tail.lstrip('0'), len(tail.lstrip('0')))

        # Lakhs (1,00,000 range) - 6th & 7th digits
        elif n_len >= 6:
            split_idx = n_len - 5
            head = remaining_str[:split_idx]
            tail = remaining_str[split_idx:]
            
            parts.append(num_to_words(head))
            parts.append(MAGNITUDE_MAP[5][0])
            decompose(tail.lstrip('0'), len(tail.lstrip('0')))

        # Thousands (1,000 range) - 4th & 5th digits
        elif n_len >= 4:
            split_idx = n_len - 3
            head = remaining_str[:split_idx]
            tail = remaining_str[split_idx:]
            
            parts.append(num_to_words(head))
            parts.append(MAGNITUDE_MAP[3][0])
            decompose(tail.lstrip('0'), len(tail.lstrip('0')))

        # Hundreds (100 range) - 3rd digit
        elif n_len == 3:
            head = remaining_str[0]
            tail = remaining_str[1:]
            
            if head in HUNDRED_PREFIXES:
                parts.append(HUNDRED_PREFIXES[head][0])
            else:
                parts.append(NUMBER_WORDS[head][0])
                parts.append(MAGNITUDE_MAP[2][0])
                
            decompose(tail.lstrip('0'), len(tail.lstrip('0')))

        # Tens & Units (0-99)
        elif n_len <= 2:
            if remaining_str in NUMBER_WORDS:
                parts.append(NUMBER_WORDS[remaining_str][0])
            else:
                pass # Should not happen for 0-99 based on map coverage

    decompose(s_num, length)
    return " ".join([p for p in parts if p])

def ordinal_str(num):
    """
    Converts a number/string to ordinal text.
    1 -> 'প্রথম', 42 -> 'বিয়াল্লিশতম'
    """
    s_num = str(num).strip()
    
    # Direct mapping for 1-10
    if s_num in ORDINAL_MAP:
        return ORDINAL_MAP[s_num]
    
    # General Rule: Cardinal + 'তম'
    return f"{num_to_words(s_num)}তম"
