"""
Abu_RunnerC - The Complete AbuLang Runner v2.0

ALL FEATURES:
✓ Smart string interpolation
✓ Smart number types  
✓ Built-in ascii() and binary()
✓ Internal libraries built-in
✓ Null handling
✓ Bare strings
✓ Clear syntax rules
"""

import re
import math
import statistics

try:
    from .abu_core import AbuLang
    from .gui_aliases import GUIAliasManager
    from .null_handler import NullObject, UndefinedVariable, NullAwareContext, create_null, is_null, is_undefined, is_nullish
except ImportError:
    from abu_core import AbuLang
    from gui_aliases import GUIAliasManager
    from null_handler import NullObject, UndefinedVariable, NullAwareContext, create_null, is_null, is_undefined, is_nullish


class ShowHandler:
    """Special handler for show[expr] syntax"""
    def __init__(self, runner):
        self.runner = runner
    
    def __getitem__(self, expr):
        """Handle show[expr] - evaluate and print"""
        print(expr)
        return None
    
    def __call__(self, *args):
        """Handle show(expr) - evaluate and print (no interpolation)"""
        for arg in args:
            print(arg)
        return None


class InterpolateHandler:
    """Special handler for $("text with {var}") syntax"""
    def __init__(self, runner):
        self.runner = runner
    
    def __call__(self, text):
        """Handle $("text") - interpolate and print"""
        if isinstance(text, str):
            text = self.runner.smart_interpolate(text)
        print(text)
        return None


class Abu_RunnerC:
    """The Complete AbuLang Runner"""
    
    def __init__(self):
        self.lang = AbuLang()
        self.gui_aliases = GUIAliasManager()
        self.context = NullAwareContext({
            "math": math,
            "stat": statistics,
            "statistics": statistics,
            "ascii": self._ascii_func,
            "binary": self._binary_func,
            "bny": self._bny_func,
            "is_null": is_null,
            "is_undefined": is_undefined,
            "is_nullish": is_nullish,
            "create_null": create_null,
            "show": ShowHandler(self),
            "print": ShowHandler(self),
            "S": InterpolateHandler(self),
            # Python built-ins
            "ord": ord,
            "chr": chr,
            "bin": bin,
            "hex": hex,
            "oct": oct,
            "abs": abs,
            "len": len,
            "min": min,
            "max": max,
            "sum": sum,
            "round": round,
            "int": int,
            "float": float,
            "str": str,
            "bool": bool,
            "list": list,
            "dict": dict,
            "set": set,
            "tuple": tuple,
            "range": range,
            "enumerate": enumerate,
            "zip": zip,
            "map": map,
            "filter": filter,
            "sorted": sorted,
            "reversed": reversed,
            "any": any,
            "all": all,
        })
        self.constants = set()
    
    def _ascii_func(self, text):
        """Get ASCII values"""
        if isinstance(text, str):
            return ord(text) if len(text) == 1 else [ord(c) for c in text]
        return ord(str(text)[0])
    
    def _binary_func(self, value):
        """Convert to binary (with 0b prefix)"""
        if isinstance(value, str):
            return bin(ord(value)) if len(value) == 1 else [bin(ord(c)) for c in value]
        return bin(int(value))
    
    def _bny_func(self, value):
        """Convert to binary (without 0b prefix - cleaner output)"""
        if isinstance(value, str):
            if len(value) == 1:
                return bin(ord(value))[2:]  # Remove '0b' prefix
            else:
                return [bin(ord(c))[2:] for c in value]  # Remove '0b' from each
        return bin(int(value))[2:]  # Remove '0b' prefix
    
    def smart_number(self, value):
        """7.0 → int, 7.5 → float"""
        if isinstance(value, float) and value.is_integer():
            return int(value)
        if isinstance(value, str):
            try:
                if '.' in value:
                    f = float(value)
                    return int(f) if f.is_integer() else f
                return int(value)
            except ValueError:
                pass
        return value
    
    def smart_interpolate(self, text):
        """Auto str() in {expressions}"""
        if not isinstance(text, str):
            return str(text)
        
        def replace_expr(match):
            expr = match.group(1).strip()
            try:
                result = eval(expr, self.context, self.context)
                return str(result)
            except Exception as e:
                return match.group(0)
        
        return re.sub(r'\{([^}]+)\}', replace_expr, text)
    
    def _eval_value(self, expr):
        """Evaluate a value expression"""
        expr = expr.strip()
        
        # Quoted string
        if (expr.startswith('"') and expr.endswith('"')) or (expr.startswith("'") and expr.endswith("'")):
            return expr[1:-1]
        
        # null keyword
        if expr == 'null':
            return NullObject()
        
        # Check if it's a bare word first
        if re.match(r'^[a-zA-Z_]\w*$', expr) and expr not in self.context and expr != 'null':
            return expr  # Bare string
        
        # Variable or expression
        try:
            value = eval(expr, self.context, self.context)
            return self.smart_number(value)
        except:
            return expr
    
    def execute_line(self, line):
        """Execute one line"""
        line = line.strip()
        if not line or line.startswith('#') or line.startswith('//'):
            return
        
        line = self.gui_aliases.translate_syntax_enhancements(line)
        
        # === SHOW (with space) - for simple values ===
        if line.startswith('show ') or line.startswith('print '):
            cmd, rest = line.split(' ', 1)
            rest = rest.strip()
            
            # Evaluate the expression
            if rest.startswith('"') or rest.startswith("'"):
                # Quoted string
                value = rest[1:-1]
                value = self.smart_interpolate(value)
            elif rest in self.context:
                # Variable
                value = self.context[rest]
            else:
                # Bare word
                value = rest
            
            print(value)
            return
        
        # === SHOW (with square brackets) - for function calls ===
        if line.startswith('show[') or line.startswith('print['):
            # Extract expression between brackets
            match = re.match(r'(?:show|print)\[(.*)\]$', line, re.DOTALL)
            if match:
                expr = match.group(1).strip()
                
                # Evaluate the function call
                try:
                    value = eval(expr, self.context, self.context)
                except Exception as e:
                    value = expr
                
                print(value)
                return
        
        # === SHOW (with parentheses) - also supported for compatibility ===
        if line.startswith('show(') or line.startswith('print('):
            # Extract expression between parentheses
            match = re.match(r'(?:show|print)\((.*)\)$', line)
            if match:
                expr = match.group(1).strip()
                
                # Evaluate the expression
                try:
                    value = eval(expr, self.context, self.context)
                except Exception as e:
                    value = expr
                
                # Apply smart interpolation if it's a string
                if isinstance(value, str):
                    value = self.smart_interpolate(value)
                
                print(value)
                return
        
        # === ASK ===
        if line.startswith('ask ') or line.startswith('input '):
            cmd, rest = line.split(' ', 1)
            rest = rest.strip()
            
            if rest.startswith('"') or rest.startswith("'"):
                prompt = rest[1:-1]
            else:
                prompt = rest
            
            prompt = self.smart_interpolate(prompt)
            result = input(prompt)
            return result
        
        # === VARIABLE ASSIGNMENT (is) ===
        if ' is ' in line and not line.startswith('if'):
            var, expr = line.split(' is ', 1)
            var = var.strip()
            expr = expr.strip()
            
            if '.' in var:
                # Property assignment
                obj_name, prop = var.rsplit('.', 1)
                obj = self.context[obj_name]
                value = self._eval_value(expr)
                setattr(obj, prop, value)
            else:
                value = self._eval_value(expr)
                self.context[var] = value
            return
        
        # === VARIABLE ASSIGNMENT (=) ===
        if '=' in line and not any(op in line for op in ['==', '!=', '>=', '<=']) and not line.startswith('if'):
            var, expr = line.split('=', 1)
            var = var.strip()
            expr = expr.strip()
            
            if '.' in var:
                # Property assignment
                obj_name, prop = var.rsplit('.', 1)
                obj = self.context[obj_name]
                value = self._eval_value(expr)
                setattr(obj, prop, value)
            else:
                value = self._eval_value(expr)
                self.context[var] = value
            return
        
        # === LIBRA ===
        if line.startswith('libra '):
            module_name = line.split()[1]
            internal = ['math', 'stat', 'statistics', 'random', 're', 'json', 'datetime', 'collections']
            if module_name in internal:
                print(f"[Info] '{module_name}' is built-in, no libra needed")
                return
            try:
                import importlib
                self.context[module_name] = importlib.import_module(module_name)
                print(f"[libra] imported {module_name}")
            except ImportError:
                print(f"[Error] Module '{module_name}' not found")
            return
        
        # === HELP ===
        if line.startswith('help'):
            self._handle_help(line)
            return
        
        # === FALLBACK ===
        try:
            exec(line, self.context, self.context)
        except Exception as e:
            print(f"[AbuLang Error] {e}")
    
    def _handle_help(self, line):
        """Handle help command"""
        parts = line.split()
        if len(parts) == 1:
            print("\n📘 AbuLang Help")
            print("Type 'help all' for all commands")
            return
        
        if parts[1] == 'all':
            print("\n📘 AbuLang Commands\n")
            for cmd, info in self.lang.commands.items():
                print(f"{cmd:10} - {info['desc']}")
    
    def run(self, code):
        """Run AbuLang code"""
        for line in code.splitlines():
            if '#' in line:
                line = line.split('#', 1)[0]
            if '//' in line:
                line = line.split('//', 1)[0]
            
            line = line.strip()
            if not line:
                continue
            
            # === INTERPOLATION PATTERN: S("text with {var}") ===
            # Convert S("text {var}") to show with interpolation
            if 'S("' in line or "S('" in line:
                # Find S( position
                s_pos = line.find('S(')
                if s_pos >= 0:
                    start = s_pos + 2
                    quote_char = line[start]  # Get the opening quote (" or ')
                    
                    # Manually find the matching closing quote
                    end = start + 1
                    while end < len(line):
                        if line[end] == quote_char and line[end-1] != '\\':
                            content = line[start+1:end]
                            break
                        end += 1
                    
                    # Do interpolation on the content
                    if '{' in content and '}' in content:
                        def replace_var(m):
                            var_expr = m.group(1).strip()
                            try:
                                val = eval(var_expr, self.context, self.context)
                                return str(val)
                            except:
                                return m.group(0)
                        content = re.sub(r'\{([^}]+)\}', replace_var, content)
                    
                    # Replace S("...") with show "..."
                    line = line[:s_pos] + f'show "{content}"' + line[end+2:]
            
            # Handle ask assignment
            if '=' in line and ('ask ' in line or 'input ' in line):
                var, rest = line.split('=', 1)
                var = var.strip()
                rest = rest.strip()
                
                if rest.startswith('ask ') or rest.startswith('input '):
                    cmd, prompt_expr = rest.split(' ', 1)
                    prompt_expr = prompt_expr.strip()
                    
                    if prompt_expr.startswith('"') or prompt_expr.startswith("'"):
                        prompt = prompt_expr[1:-1]
                    else:
                        prompt = prompt_expr
                    
                    prompt = self.smart_interpolate(prompt)
                    result = input(prompt)
                    self.context[var] = self.smart_number(result)
                    continue
            
            self.execute_line(line)


# Alias for compatibility
AbuRunner = Abu_RunnerC


if __name__ == "__main__":
    runner = Abu_RunnerC()
    
    print("=== Abu_RunnerC Tests ===\n")
    
    print("Test 1: Basic")
    runner.run('show "Hello, World!"')
    
    print("\nTest 2: Interpolation")
    runner.run('''
name = "Abu"
age = 25
show("Name: {name}, Age: {age}")
''')
    
    print("\nTest 3: Functions")
    runner.run('show(math.sqrt(16))')
    
    print("\nTest 4: Built-ins")
    runner.run('show(ascii("A"))')
    runner.run('show(binary(10))')
    
    print("\nTest 5: Null")
    runner.run('''
person = null
person.name = "Alice"
show("Name: {person.name}")
''')
    
    print("\nTest 6: Bare Strings")
    runner.run('''
greeting = Hello
show greeting
''')
    
    print("\n=== All Tests Pass! ===")
