import os
import importlib.util
import shutil
import io
import re
import inspect
from contextlib import redirect_stdout
from colorama import Fore, Style, init

init(autoreset=True)

class Pytterns:
    def __init__(self):
        self.internal_dir = os.path.join(os.path.dirname(__file__), "patterns")
        self.user_dir = "patterns"
        self.BANNED_WORDS = [
            "os.", "subprocess", "eval(", "exec(", "open(", 
            "shutil", "getattr", "setattr", "__subclasses__", 
            "builtins", "pickle", "socket", "requests", "importlib",
            "sys.", "threading", "multiprocessing"
        ]
        self.color_map = {
            "red": Style.BRIGHT+Fore.RED, "green": Style.BRIGHT+Fore.GREEN, 
            "blue": Style.BRIGHT+Fore.BLUE, "yellow": Style.BRIGHT+Fore.YELLOW, 
            "magenta": Style.BRIGHT+Fore.MAGENTA, "cyan": Style.BRIGHT+Fore.CYAN, 
            "white": Style.BRIGHT+Fore.WHITE, "reset": Fore.RESET
        }

    def _strip_ansi(self, text):
        return re.sub(r'\x1B(?:[@-Z\\-_]|\[[0-9;]*[ -/]*[@-~])', '', text)

    def _is_safe(self, file_path):
        try:
            with open(file_path, "r", encoding='utf-8') as f:
                content = f.read()
                for word in self.BANNED_WORDS:
                    if word in content: return False, word
            return True, None
        except: return False, "File Unreadable"

    def __getattr__(self, name):
        user_file = os.path.join(self.user_dir, f"{name}.py")
        lib_file = os.path.join(self.internal_dir, f"{name}.py")
        file_path = user_file if os.path.exists(user_file) else lib_file
        
        if not os.path.exists(file_path):
            raise AttributeError(f"Pattern '{name}' not found.")

        safe, dangerous_word = self._is_safe(file_path)
        if not safe:
            print(f"SECURITY ALERT: '{name}' contains blocked command: '{dangerous_word}'")
            return lambda *args, **kwargs: None

        spec = importlib.util.spec_from_file_location(name, file_path)
        module = importlib.util.module_from_spec(spec)
        spec.loader.exec_module(module)

        def wrapper(*args, **kwargs):
            # 1. Capture the core flags
            center_val = kwargs.get('center', False)
            color_val = kwargs.get('color', None)
            
            # Check positional args for 'center' (usually 2nd arg)
            if len(args) > 1 and isinstance(args[1], bool):
                center_val = args[1]

            ansi_prefix = self.color_map.get(str(color_val).lower(), "") if color_val else ""

            # 2. Smart Argument Mapping
            # This prevents the "Multiple values for argument" error
            sig = inspect.signature(module.draw)
            bound_args = sig.bind_partial(*args, **kwargs)
            
            # Inject internal helper variables if the pattern accepts them
            if 'ansi_prefix' in sig.parameters:
                kwargs['ansi_prefix'] = ansi_prefix
            if 'center' in sig.parameters and 'center' not in bound_args.arguments:
                kwargs['center'] = center_val

            # 3. Capture and Print
            f = io.StringIO()
            with redirect_stdout(f):
                try:
                    module.draw(*args, **kwargs)
                except Exception as e:
                    print(f"Error drawing {name}: {e}")

            output = f.getvalue().splitlines()
            term_width = shutil.get_terminal_size().columns

            for line in output:
                formatted = f"{ansi_prefix}{line}{Style.RESET_ALL if ansi_prefix else ''}"
                if center_val:
                    clean_len = len(self._strip_ansi(line))
                    pad = max(0, (term_width - clean_len) // 2)
                    print(" " * pad + formatted, flush=True)
                else:
                    print(formatted, flush=True)
        return wrapper