"""
CSSL Bridge - Python API for CSSL Language
Provides CsslLang class for executing CSSL code from Python.
"""

import threading
from pathlib import Path
from typing import Any, List, Optional, Callable, Dict


class CSSLModule:
    """
    A callable CSSL module that executes code with arguments.

    Created via CSSL.module() - the code is executed each time the module is called,
    with arguments accessible via parameter.get(index).
    """

    def __init__(self, cssl_instance: 'CsslLang', code: str):
        self._cssl = cssl_instance
        self._code = code

    def __call__(self, *args) -> Any:
        """Execute the module code with the given arguments."""
        return self._cssl.exec(self._code, *args)

    def __repr__(self) -> str:
        return f"<CSSLModule code_len={len(self._code)}>"


class CSSLFunctionModule:
    """
    A CSSL module with accessible functions as methods.

    Created via CSSL.makemodule() - functions defined in the CSSL code
    become callable attributes on this module.
    """

    def __init__(self, cssl_instance: 'CsslLang', code: str):
        self._cssl = cssl_instance
        self._code = code
        self._runtime = None
        self._functions: Dict[str, Any] = {}
        self._initialized = False

    def _ensure_initialized(self):
        """Initialize the module by parsing and registering functions."""
        if self._initialized:
            return

        from .cssl import CSSLRuntime, parse_cssl_program, ASTNode

        # Create a dedicated runtime for this module
        self._runtime = CSSLRuntime()

        # Parse the code
        ast = parse_cssl_program(self._code)

        # Execute to register all function definitions
        for child in ast.children:
            if child.type == 'function':
                func_info = child.value
                func_name = func_info.get('name')
                self._functions[func_name] = child
                self._runtime.scope.set(func_name, child)
            else:
                # Execute other statements (like struct definitions)
                try:
                    self._runtime._execute_node(child)
                except Exception:
                    pass

        self._initialized = True

    def __getattr__(self, name: str) -> Callable:
        """Get a function from the module."""
        # Avoid recursion for internal attributes
        if name.startswith('_'):
            raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")

        self._ensure_initialized()

        if name in self._functions:
            func_node = self._functions[name]

            def wrapper(*args):
                from .cssl import Parameter
                # Set up parameter object for this call
                self._runtime.global_scope.set('parameter', Parameter(list(args)))
                self._runtime.global_scope.set('args', list(args))
                self._runtime.global_scope.set('argc', len(args))
                return self._runtime._call_function(func_node, list(args))

            return wrapper

        raise AttributeError(f"CSSL module has no function '{name}'")

    def __dir__(self) -> List[str]:
        """List available functions."""
        self._ensure_initialized()
        return list(self._functions.keys())

    def __repr__(self) -> str:
        self._ensure_initialized()
        funcs = ', '.join(self._functions.keys())
        return f"<CSSLFunctionModule functions=[{funcs}]>"


class CsslLang:
    """
    CSSL Language interface for Python.

    Usage:
        from includecpp import CSSL
        cssl = CSSL.CsslLang()
        result = cssl.exec("script.cssl", arg1, arg2)
        cssl.T_exec("async_script.cssl", arg1)  # Threaded
    """

    def __init__(self, output_callback: Optional[Callable[[str, str], None]] = None):
        """
        Initialize CSSL runtime.

        Args:
            output_callback: Optional callback for output (text, level)
        """
        self._output_callback = output_callback
        self._runtime = None
        self._threads: List[threading.Thread] = []

    def _get_runtime(self):
        """Lazy load CSSL runtime."""
        if self._runtime is None:
            from .cssl import CSSLRuntime
            self._runtime = CSSLRuntime(output_callback=self._output_callback)
        return self._runtime

    def exec(self, path_or_code: str, *args) -> Any:
        """
        Execute CSSL code or file.

        Args:
            path_or_code: Path to .cssl file or CSSL code string
            *args: Arguments to pass to the script

        Returns:
            Execution result. If parameter.return() was called, returns
            the list of returned values (or single value if only one).
        """
        runtime = self._get_runtime()

        # Check if it's a file path
        path = Path(path_or_code)
        if path.exists() and path.suffix in ('.cssl', '.cssl-mod'):
            source = path.read_text(encoding='utf-8')
        else:
            source = path_or_code

        # Set arguments in runtime scope
        from .cssl import Parameter
        param = Parameter(list(args))
        runtime.global_scope.set('args', list(args))
        runtime.global_scope.set('argc', len(args))
        runtime.global_scope.set('parameter', param)

        # Execute as standalone program
        try:
            result = runtime.execute_program(source)

            # Check if parameter.return() was used (generator-like returns)
            if param.has_returns():
                returns = param.returns()
                # Return single value if only one, else return list
                return returns[0] if len(returns) == 1 else returns

            return result
        except Exception as e:
            raise RuntimeError(f"CSSL Error: {e}") from e

    def T_exec(self, path_or_code: str, *args, callback: Optional[Callable[[Any], None]] = None) -> threading.Thread:
        """
        Execute CSSL code asynchronously in a thread.

        Args:
            path_or_code: Path to .cssl file or CSSL code string
            *args: Arguments to pass to the script
            callback: Optional callback when execution completes

        Returns:
            Thread object
        """
        def _run():
            try:
                result = self.exec(path_or_code, *args)
                if callback:
                    callback(result)
            except Exception as e:
                if callback:
                    callback(e)

        thread = threading.Thread(target=_run, daemon=True)
        thread.start()
        self._threads.append(thread)
        return thread

    def wait_all(self, timeout: Optional[float] = None):
        """Wait for all async executions to complete."""
        for thread in self._threads:
            thread.join(timeout=timeout)
        self._threads.clear()

    def get_output(self) -> List[str]:
        """Get output buffer from last execution."""
        runtime = self._get_runtime()
        return list(runtime.output_buffer)

    def clear_output(self):
        """Clear output buffer."""
        runtime = self._get_runtime()
        runtime.output_buffer.clear()

    def set_global(self, name: str, value: Any):
        """Set a global variable in CSSL runtime."""
        runtime = self._get_runtime()
        runtime.global_scope.set(name, value)

    def get_global(self, name: str) -> Any:
        """Get a global variable from CSSL runtime."""
        runtime = self._get_runtime()
        return runtime.global_scope.get(name)

    def module(self, code: str) -> 'CSSLModule':
        """
        Create a callable CSSL module from code.

        The module can be called with arguments that are passed to the CSSL code.

        Usage:
            module = CSSL.module('''
                printl(parameter.get(0));
            ''')
            module("Hello")  # Prints "Hello"

        Args:
            code: CSSL code string

        Returns:
            CSSLModule - a callable module
        """
        return CSSLModule(self, code)

    def makemodule(self, code: str) -> 'CSSLFunctionModule':
        """
        Create a CSSL module with accessible functions.

        Functions defined in the code become methods on the returned module.

        Usage:
            module = CSSL.makemodule('''
                string greet(string name) {
                    return "Hello, " + name + "!";
                }

                int add(int a, int b) {
                    return a + b;
                }
            ''')
            module.greet("World")  # Returns "Hello, World!"
            module.add(2, 3)       # Returns 5

        Args:
            code: CSSL code string with function definitions

        Returns:
            CSSLFunctionModule - module with callable function attributes
        """
        return CSSLFunctionModule(self, code)

    def code(self, name: str, code: str) -> None:
        """
        Register inline CSSL code as a payload that can be loaded via payload().

        This allows creating payloads from Python code without external files.

        Usage:
            from includecpp import CSSL
            cssl = CSSL.CsslLang()

            # Register a helper payload
            cssl.code("helpers", '''
                global version = "1.0.0";
                void log(string msg) {
                    printl("[LOG] " + msg);
                }
            ''')

            # Use it in CSSL code
            cssl.exec('''
                payload("helpers");  // Load the inline payload
                @log("Hello!");      // Call the helper function
                printl(@version);    // Access the global
            ''')

        Args:
            name: Name to register the payload under (used in payload("name"))
            code: CSSL code string
        """
        runtime = self._get_runtime()
        if not hasattr(runtime, '_inline_payloads'):
            runtime._inline_payloads = {}
        runtime._inline_payloads[name] = code


# Singleton for convenience
_default_instance: Optional[CsslLang] = None

def get_cssl() -> CsslLang:
    """Get default CSSL instance."""
    global _default_instance
    if _default_instance is None:
        _default_instance = CsslLang()
    return _default_instance


# Module-level convenience functions
def exec(path_or_code: str, *args) -> Any:
    """
    Execute CSSL code or file.

    Usage:
        from includecpp import CSSL
        CSSL.exec("script.cssl", arg1, arg2)
        CSSL.exec("printl('Hello World');")

    Args:
        path_or_code: Path to .cssl file or CSSL code string
        *args: Arguments to pass to the script

    Returns:
        Execution result
    """
    return get_cssl().exec(path_or_code, *args)


def T_exec(path_or_code: str, *args, callback: Optional[Callable[[Any], None]] = None) -> threading.Thread:
    """
    Execute CSSL code asynchronously in a thread.

    Usage:
        from includecpp import CSSL
        CSSL.T_exec("async_script.cssl", arg1, callback=on_done)

    Args:
        path_or_code: Path to .cssl file or CSSL code string
        *args: Arguments to pass to the script
        callback: Optional callback when execution completes

    Returns:
        Thread object
    """
    return get_cssl().T_exec(path_or_code, *args, callback=callback)


def set_global(name: str, value: Any) -> None:
    """Set a global variable in CSSL runtime."""
    get_cssl().set_global(name, value)


def get_global(name: str) -> Any:
    """Get a global variable from CSSL runtime."""
    return get_cssl().get_global(name)


def get_output() -> List[str]:
    """Get output buffer from last execution."""
    return get_cssl().get_output()


def clear_output() -> None:
    """Clear output buffer."""
    get_cssl().clear_output()


# Aliases to avoid conflict with Python builtin exec
_exec = exec
_T_exec = T_exec


def module(code: str) -> CSSLModule:
    """
    Create a callable CSSL module from code.

    Usage:
        from includecpp import CSSL
        greet = CSSL.module('''
            printl("Hello, " + parameter.get(0) + "!");
        ''')
        greet("World")  # Prints "Hello, World!"

    Args:
        code: CSSL code string

    Returns:
        CSSLModule - a callable module
    """
    return get_cssl().module(code)


def makemodule(code: str) -> CSSLFunctionModule:
    """
    Create a CSSL module with accessible functions.

    Usage:
        from includecpp import CSSL
        math_mod = CSSL.makemodule('''
            int add(int a, int b) {
                return a + b;
            }

            int multiply(int a, int b) {
                return a * b;
            }
        ''')
        math_mod.add(2, 3)       # Returns 5
        math_mod.multiply(4, 5)  # Returns 20

    Args:
        code: CSSL code string with function definitions

    Returns:
        CSSLFunctionModule - module with callable function attributes
    """
    return get_cssl().makemodule(code)


# Export all
__all__ = [
    'CsslLang',
    'CSSLModule',
    'CSSLFunctionModule',
    'get_cssl',
    'exec',
    '_exec',  # Alias for exec (avoids Python builtin conflict)
    'T_exec',
    '_T_exec',  # Alias for T_exec
    'set_global',
    'get_global',
    'get_output',
    'clear_output',
    'module',
    'makemodule',
]
