from abstract_utiliuties import *
# attach_functions.py  — single helper you can import anywhere
# attach_dynamic.py
from __future__ import annotations
from types import ModuleType
from typing import Iterable
from abstract_utilities.robust_readers.file_filters import define_defaults,get_files_and_dirs

import inspect
from abstract_utilities.read_write_utils import *
import textwrap, pkgutil, os, re, textwrap, sys, types, importlib, importlib.util, inspect
from typing import *
ABSPATH = os.path.abspath(__file__)
ABSROOT = os.path.dirname(ABSPATH)
def get_caller_path():
    frame = inspect.stack()[1]
    return os.path.abspath(frame.filename)
def get_caller_dir():
    frame = inspect.stack()[1]
    abspath = os.path.abspath(frame.filename)
    return os.path.dirname(abspath)
def call_for_all_tabs():

    root = get_caller_dir()
    get_for_all_tabs(root)


def clean_imports():
    alls = str(list(set("""os,re,subprocess,sys,re,traceback,pydot, enum, inspect, sys, traceback, threading,json,traceback,logging,requests""".replace('\n','').replace(' ','').replace('\t','').split(','))))[1:-1].replace('"','').replace("'",'')
    input(alls)
def isTab(item):
    item_lower =  item.lower()
    for key in ['console','tab']:
        if item_lower.endswith(key):
            return True
def get_dir(root,item):
    if None in [root]:
        return None
    path = root
    if item != None:
        path = os.path.join(path,item)
    return path
def isDir(root,item=None):
    path = get_dir(root,item)
    if path:
        return os.path.isdir(path)
def check_dir_item(root,item=None):
    return item and isTab(item) and isDir(root,item)
def get_dirs(root = None):
    root = root or ABSROOT
    dirpaths = [get_dir(root,item) for item in os.listdir(root) if check_dir_item(root,item)]
    return dirpaths
def ifFunctionsInFile(root):
    items = [os.path.join(root, "functions"),os.path.join(root, "functions.py")]
    for item in items:
        if os.path.exists(item):
            return item
        

def get_for_all_tabs(root = None):
    root = root or caller_path()
    if os.path.isfile(root):
        root = os.path.dirname(root)
    all_tabs = get_dirs(root = root)
    for ROOT in all_tabs:
        FUNCS_DIR = ifFunctionsInFile(ROOT)
        if FUNCS_DIR == None:
            for ROOT in get_dirs(root = ROOT):
                apply_inits(ROOT)
        else: 
            apply_inits(ROOT)
            
def extract_funcs(path: str):
    funcs = []
    for line in read_from_file(path).splitlines():
        m = re.match(r"^def\s+([A-Za-z_]\w*)\s*\(self", line)
        if m:
            funcs.append(m.group(1))
    return funcs
def extract_classes(path: str):
    funcs = []
    for line in read_from_file(path).splitlines():
        m = re.match(r"^class\s+:", line)
        if m:
            funcs.append(m.group(1))
    return funcs
def extract_import(path: str):
    funcs = []
    for line in read_from_file(path).splitlines():
        m = re.match(r"^import\s+[A-Za-z_]\w*\s*\", line)
        if m:
            funcs.append(m.group(1))
    return funcs
def extract_from(path: str):
    funcs = []
    for line in read_from_file(path).splitlines():
        m = re.match(r"^from\s+[A-Za-z_]\w*\s*\", line)
        if m:
            funcs.append(m.group(1))
    return funcs
def get_file_paths(directory):
    if if_fun_dir != None:
        if if_fun_dir:
            CFG = define_defaults(allowed_exts='.py',
                unallowed_exts = True,
                exclude_types = True,
                exclude_dirs = True,
                exclude_patterns = True)
            _,filepaths = get_files_and_dirs(directory,cfg=CFG)
            
        else:
            filepaths = [FUNCS_DIR]
    return filepaths
def run_py_data(directory):
    if os.path.isfile(directory):
        directory = os.path.dirname(directory)
    filepaths = get_file_paths(directory)
    for filepath in enumerate(filepaths):
        text = None
        data[filepath]={}
        if os.path.isfile(init_path):
            text = read_from_file(init_path)
        if text:
            data[filepath]["funcs"] = extract_funcs(text)
            data[filepath]["classes"] = extract_classes(text)
            data[filepath]["import"] = extract_import(text)
            data[filepath]["from"] = extract_from(text)
        data 
def get_init_data(directory):
    if os.path.isfile(directory):
        directory = os.path.dirname(directory)
    init_path = os.path.join(directory,'__init__.py')
    data = {}
    text=""
    if os.path.isfile(init_path):
        text = read_from_file(init_path)
 
    input(data)
    return data
def apply_inits(ROOT):
    FUNCS_DIR = ifFunctionsInFile(ROOT)

    
    if_fun_dir = isDir(FUNCS_DIR)
        filepaths = get_file_paths(FUNCS_DIR)
        
    
        
        # Build functions/__init__.py that re-exports all discovered functions
        import_lines = []
        all_funcs = []
        covered_dirs = []
        for fp in filepaths:
            dirname = os.path.dirname(fp)
            if dirname not in covered_dirs:
                covered_dirs.append(dirname)
            get_init_data(directory)
            module = os.path.splitext(os.path.basename(fp))[0]
            funcs = extract_funcs(fp)
            if funcs:
                import_lines.append(f"from .{module} import ({', '.join(funcs)})")
                all_funcs.extend(funcs)
        if if_fun_dir:
            functions_init = "\n".join(import_lines) + ("\n" if import_lines else "")
            write_to_file(contents=functions_init, file_path=os.path.join(FUNCS_DIR, "__init__.py"))

        # Prepare the tuple literal of function names for import + loop
        uniq_funcs = sorted(set(all_funcs))
        func_tuple = ", ".join(uniq_funcs) + ("," if len(uniq_funcs) == 1 else "")
        
        # Generate apiConsole/initFuncs.py using the safer setattr-loop
        init_funcs_src = textwrap.dedent(f"""\
            

            from .functions import ({func_tuple})

            def initFuncs(self):
                try:
                    for f in ({func_tuple}):
                        setattr(self, f.__name__, f)
                except Exception as e:
                    logger.info(f"{{e}}")
                return self
        """)

        write_to_file(contents=init_funcs_src, file_path=os.path.join(ROOT, "initFuncs.py"))

