import sys
import os
import time
import functools
from contextlib import contextmanager
from .colors import ConsoleColors


def log_calls(logger=None, level="DEBUG", include_args=True, include_result=False):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            func_name = func.__name__
            
            if include_args:
                args_repr = [repr(a) for a in args]
                kwargs_repr = [f"{k}={v!r}" for k, v in kwargs.items()]
                signature = ", ".join(args_repr + kwargs_repr)
                msg = f"Calling {func_name}({signature})"
            else:
                msg = f"Calling {func_name}()"
            
            if logger:
                getattr(logger, level.lower(), logger.debug)(msg)
            else:
                print(f"{ConsoleColors.GRAY}[{level}]{ConsoleColors.RESET} {msg}")
            
            result = func(*args, **kwargs)
            
            if include_result:
                result_msg = f"{func_name} returned {result!r}"
                if logger:
                    getattr(logger, level.lower(), logger.debug)(result_msg)
                else:
                    print(f"{ConsoleColors.GRAY}[{level}]{ConsoleColors.RESET} {result_msg}")
            
            return result
        return wrapper
    return decorator


def timer(logger=None, name=None):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start = time.perf_counter()
            result = func(*args, **kwargs)
            elapsed = time.perf_counter() - start
            
            display_name = name or func.__name__
            
            if elapsed < 0.001:
                time_str = f"{elapsed * 1000000:.2f}μs"
            elif elapsed < 1:
                time_str = f"{elapsed * 1000:.2f}ms"
            else:
                time_str = f"{elapsed:.3f}s"
            
            msg = f"⏱ {display_name}: {time_str}"
            
            if logger:
                logger.info(msg)
            else:
                print(f"{ConsoleColors.PURPLE}{msg}{ConsoleColors.RESET}")
            
            return result
        return wrapper
    return decorator


def retry(max_attempts=3, delay=1, exceptions=(Exception,), logger=None):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            last_exception = None
            for attempt in range(1, max_attempts + 1):
                try:
                    return func(*args, **kwargs)
                except exceptions as e:
                    last_exception = e
                    msg = f"Attempt {attempt}/{max_attempts} failed: {e}"
                    if logger:
                        logger.warning(msg)
                    else:
                        print(f"{ConsoleColors.YELLOW}[WARNING]{ConsoleColors.RESET} {msg}")
                    if attempt < max_attempts:
                        time.sleep(delay)
            raise last_exception
        return wrapper
    return decorator


@contextmanager
def LogContext(logger, context_name):
    old_context = logger.context
    logger.set_context(context_name)
    try:
        yield logger
    finally:
        if old_context:
            logger.set_context(old_context)
        else:
            logger.clear_context()


@contextmanager
def suppress_output():
    old_stdout = sys.stdout
    old_stderr = sys.stderr
    try:
        sys.stdout = open(os.devnull, 'w')
        sys.stderr = open(os.devnull, 'w')
        yield
    finally:
        sys.stdout.close()
        sys.stderr.close()
        sys.stdout = old_stdout
        sys.stderr = old_stderr


def supports_ansi():
    if not hasattr(sys.stdout, "isatty"):
        return False
    if not sys.stdout.isatty():
        return False
    if sys.platform == "win32":
        try:
            import ctypes
            kernel32 = ctypes.windll.kernel32
            kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
            return True
        except Exception:
            return os.environ.get("TERM") is not None
    return True


def get_terminal_size(fallback=(80, 24)):
    try:
        import shutil
        size = shutil.get_terminal_size(fallback=fallback)
        return size.columns, size.lines
    except Exception:
        return fallback


def strip_ansi(text):
    import re
    ansi_pattern = re.compile(r'\033\[[0-9;]*m')
    return ansi_pattern.sub('', text)


def truncate(text, max_length, suffix="..."):
    if len(text) <= max_length:
        return text
    return text[:max_length - len(suffix)] + suffix


def indent(text, prefix="  "):
    lines = text.split("\n")
    return "\n".join(prefix + line for line in lines)


def wrap_text(text, width=80):
    words = text.split()
    lines = []
    current_line = []
    current_length = 0
    
    for word in words:
        if current_length + len(word) + len(current_line) > width:
            lines.append(" ".join(current_line))
            current_line = [word]
            current_length = len(word)
        else:
            current_line.append(word)
            current_length += len(word)
    
    if current_line:
        lines.append(" ".join(current_line))
    
    return "\n".join(lines)


def colorize(text, color):
    return f"{color}{text}{ConsoleColors.RESET}"


def bold(text):
    return f"{ConsoleColors.BOLD}{text}{ConsoleColors.RESET}"


def dim(text):
    return f"{ConsoleColors.DIM}{text}{ConsoleColors.RESET}"


def italic(text):
    return f"{ConsoleColors.ITALIC}{text}{ConsoleColors.RESET}"


def underline(text):
    return f"{ConsoleColors.UNDERLINE}{text}{ConsoleColors.RESET}"


def strikethrough(text):
    return f"{ConsoleColors.STRIKETHROUGH}{text}{ConsoleColors.RESET}"
