import time
import functools
import os
from .client import MonitoringFacade
from typing import Optional, Dict

def trace_function(
    name: str = "function.execution_time",
    resource: Optional[str] = None,
    service: Optional[str] = None,
    tags: Optional[Dict[str, str]] = None,
    capture_args: bool = False,
):
    """
    Decorator to wrap a function execution in a Datadog Span.

    Args:
        name:         The span operation name (e.g. "web.request").
        resource:     The resource/endpoint name. Defaults to the function name.
        service:      The service name shown in the Datadog Trace Explorer.
                      Falls back to the DD_SERVICE env var set at process start.
                      If neither is set, ddtrace will show "unnamed-python-service".
        tags:         Extra key/value tags to attach to the span.
        capture_args: If True, captures kwargs as span tags (prefixed with "arg.").
    """
    service_name = service or os.getenv("DD_SERVICE")

    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                from ddtrace import tracer
                res_name = resource if resource else func.__name__
                context = tracer.trace(name, service=service_name, resource=res_name)
            except ImportError:
                from contextlib import nullcontext
                context = nullcontext()

            with context as span:
                if span:
                    if tags:
                        span.set_tags(tags)
                    if capture_args:
                        # Capture kwargs as tags
                        for k, v in kwargs.items():
                            span.set_tag(f"arg.{k}", str(v))

                return func(*args, **kwargs)
        return wrapper
    return decorator

def monitor_execution_time(metric_name: str = "function.execution_time", tags: Optional[Dict[str, str]] = None):
    """
    Decorator to measure execution time and send it as a histogram metric.
    """
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            try:
                result = func(*args, **kwargs)
                return result
            finally:
                duration = (time.time() - start_time)
                current_tags = tags.copy() if tags else {}
                current_tags["function"] = func.__name__

                MonitoringFacade.get_instance().send(metric_name, duration, metric_type='histogram', tags=current_tags)
        return wrapper
    return decorator

def monitor_errors(metric_name: str = "function.errors", tags: Optional[Dict[str, str]] = None):
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            try:
                return func(*args, **kwargs)
            except Exception as e:
                current_tags = tags.copy() if tags else {}
                current_tags["function"] = func.__name__
                current_tags["error"] = type(e).__name__

                MonitoringFacade.get_instance().send(metric_name, metric_type='increment', tags=current_tags)
                raise e
        return wrapper
    return decorator
