"""
AbuLang Bytecode Compiler - Compile code to bytecode for faster execution
"""

import marshal
import types
from typing import Dict, Any, Optional
from pathlib import Path


class BytecodeCompiler:
    """Compiles AbuLang code to bytecode"""
    
    def __init__(self):
        self.cache: Dict[str, types.CodeType] = {}
    
    def compile_code(self, code: str, filename: str = "<abulang>") -> types.CodeType:
        """Compile code to bytecode"""
        try:
            bytecode = compile(code, filename, 'exec')
            return bytecode
        except SyntaxError as e:
            raise SyntaxError(f"Failed to compile code: {e}")
    
    def cache_bytecode(self, name: str, code: str, filename: str = "<abulang>"):
        """Compile and cache bytecode"""
        bytecode = self.compile_code(code, filename)
        self.cache[name] = bytecode
    
    def get_cached_bytecode(self, name: str) -> Optional[types.CodeType]:
        """Get cached bytecode"""
        return self.cache.get(name)
    
    def execute_bytecode(self, bytecode: types.CodeType, context: Dict = None) -> Any:
        """Execute compiled bytecode"""
        if context is None:
            context = {}
        
        exec(bytecode, context)
        return context
    
    def save_bytecode(self, bytecode: types.CodeType, filename: str):
        """Save bytecode to file"""
        with open(filename, 'wb') as f:
            marshal.dump(bytecode, f)
    
    def load_bytecode(self, filename: str) -> types.CodeType:
        """Load bytecode from file"""
        with open(filename, 'rb') as f:
            return marshal.load(f)
    
    def clear_cache(self):
        """Clear bytecode cache"""
        self.cache.clear()
    
    def get_cache_stats(self) -> Dict:
        """Get cache statistics"""
        return {
            "cached_items": len(self.cache),
            "cache_size": sum(len(marshal.dumps(bc)) for bc in self.cache.values())
        }


class BytecodeCache:
    """Persistent bytecode cache"""
    
    def __init__(self, cache_dir: str = ".abuc/bytecode"):
        self.cache_dir = Path(cache_dir)
        self.cache_dir.mkdir(parents=True, exist_ok=True)
    
    def get_cache_file(self, name: str) -> Path:
        """Get cache file path"""
        return self.cache_dir / f"{name}.pyc"
    
    def save(self, name: str, bytecode: types.CodeType):
        """Save bytecode to cache"""
        cache_file = self.get_cache_file(name)
        with open(cache_file, 'wb') as f:
            marshal.dump(bytecode, f)
    
    def load(self, name: str) -> Optional[types.CodeType]:
        """Load bytecode from cache"""
        cache_file = self.get_cache_file(name)
        if cache_file.exists():
            try:
                with open(cache_file, 'rb') as f:
                    return marshal.load(f)
            except Exception:
                return None
        return None
    
    def exists(self, name: str) -> bool:
        """Check if bytecode is cached"""
        return self.get_cache_file(name).exists()
    
    def clear(self):
        """Clear all cached bytecode"""
        for cache_file in self.cache_dir.glob("*.pyc"):
            cache_file.unlink()


# Global bytecode compiler
_bytecode_compiler = BytecodeCompiler()
_bytecode_cache = BytecodeCache()


def get_bytecode_compiler() -> BytecodeCompiler:
    """Get global bytecode compiler"""
    return _bytecode_compiler


def get_bytecode_cache() -> BytecodeCache:
    """Get global bytecode cache"""
    return _bytecode_cache
