"""AgentBill Signals

Signal function for tracking business events and linking revenue to AI traces.

v8.0.0: Refactored to delegate to _emit_signal() orchestrator.
v8.3.0: Migrated from global dict to contextvars for async-safe config (Phase 4).
"""
import contextvars
from typing import Optional, Dict, Any
from .signal_builder import _emit_signal


# Async-safe configuration - set by AgentBill.init() or agentbill_tracing
# Uses contextvars.ContextVar for proper isolation across async tasks/coroutines.
# Backward-compatible: behaves like a global dict in synchronous code.
_signal_config: contextvars.ContextVar[Dict[str, Any]] = contextvars.ContextVar(
    'agentbill_signal_config', default={}
)


def set_signal_config(config: Dict[str, Any]) -> None:
    """Set configuration for signal() function (async-safe via ContextVar)."""
    _signal_config.set(config)


def get_signal_config() -> Dict[str, Any]:
    """Get current configuration (async-safe via ContextVar)."""
    return _signal_config.get()


def signal(
    event_name: str,
    revenue: Optional[float] = None,
    metadata: Optional[Dict[str, Any]] = None,
    *,
    customer_id: Optional[str] = None,
    session_id: Optional[str] = None,
    trace_id: Optional[str] = None,
    span_id: Optional[str] = None,
    parent_span_id: Optional[str] = None,
    currency: str = "USD",
    event_type: Optional[str] = None,
    event_value: Optional[float] = None,
    order_id: Optional[str] = None,
    order_external_id: Optional[str] = None,
    group_by: Optional[Dict[str, str]] = None,
    idempotency_key: Optional[str] = None,
    cost_data: Optional[Dict[str, Any]] = None,
    # ── Business Context (v10.0.0) ──────────────────────────────
    product_id: Optional[str] = None,
    feature_flag: Optional[str] = None,
    environment: Optional[str] = None,
    deployment_version: Optional[str] = None,
    region: Optional[str] = None,
    tenant_id: Optional[str] = None,
    # ── Quality Metrics (v10.0.0) ───────────────────────────────
    feedback_score: Optional[float] = None,
    user_satisfaction: Optional[float] = None,
    error_type: Optional[str] = None,
    error_message: Optional[str] = None,
    retry_count: Optional[int] = None,
    success_rate: Optional[float] = None,
    # ── Categorization (v10.0.0) ────────────────────────────────
    tags: Optional[list] = None,
    category: Optional[str] = None,
    priority: Optional[str] = None,
) -> Dict[str, Any]:
    """
    Emit a business signal/event and link it to AI traces.
    
    This is the ONLY method to record business events in AgentBill (v10.0.0+).
    Posts to /otel-collector to record conversions, purchases, signups and
    link them to AI traces via trace_id for revenue attribution.
    
    Args:
        event_name: Name of the business event (e.g., "purchase", "signup")
        revenue: Revenue amount associated with this event
        metadata: Additional metadata for the event
        customer_id: Customer ID (uses global config if not provided)
        session_id: Session ID for attribution
        trace_id: Trace ID to link to (auto-detected from context if not provided)
        span_id: Span ID (auto-generated if not provided)
        parent_span_id: Links to predecessor span
        currency: Currency for revenue (default: USD)
        event_type: Type of event for categorization
        event_value: Numeric value of the event
        order_id: Order ID to link this signal to
        order_external_id: External order ID to link this signal to
        group_by: Business grouping keys (session_id, workflow_id, batch_id, correlation_id)
        idempotency_key: Dedup key (auto-generated if not provided)
        cost_data: Manual cost/usage data (CostData dict)
        product_id: Product or feature identifier (e.g., "pro-plan")
        feature_flag: Feature flag active during this event
        environment: Deployment environment (e.g., "production")
        deployment_version: App version (e.g., "2.4.1")
        region: Geographic region (e.g., "us-east-1")
        tenant_id: Multi-tenant identifier
        feedback_score: User feedback score (e.g., 1-5)
        user_satisfaction: User satisfaction score (NPS/CSAT)
        error_type: Error type classification
        error_message: Error description
        retry_count: Number of retries
        success_rate: Success rate (0.0-1.0)
        tags: Tags for filtering and grouping
        category: Category for this signal
        priority: Priority level (high/medium/low)
        
    Returns:
        Dict with status and any response data
        
    Example:
        >>> signal("purchase", revenue=99.99, product_id="pro-plan", region="us-east-1")
        >>> signal("ai_call", cost_data={"vendor": "openai", "cost": {"amount": 0.002, "currency": "USD"}})
    """
    config = get_signal_config()
    
    return _emit_signal(
        event_name=event_name,
        config=config,
        customer_id=customer_id or config.get("customer_id"),
        agent_id=config.get("agent_id"),
        trace_id=trace_id,
        span_id=span_id,
        parent_span_id=parent_span_id,
        revenue=revenue,
        currency=currency,
        event_type=event_type,
        event_value=event_value,
        session_id=session_id,
        order_id=order_id,
        order_external_id=order_external_id,
        group_by=group_by,
        idempotency_key=idempotency_key,
        metadata=metadata,
        cost_data=cost_data,
        # v10.0.0: Business Context
        product_id=product_id,
        feature_flag=feature_flag,
        environment=environment,
        deployment_version=deployment_version,
        region=region,
        tenant_id=tenant_id,
        # v10.0.0: Quality Metrics
        feedback_score=feedback_score,
        user_satisfaction=user_satisfaction,
        error_type=error_type,
        error_message=error_message,
        retry_count=retry_count,
        success_rate=success_rate,
        # v10.0.0: Categorization
        tags=tags,
        category=category,
        priority=priority,
    )


def cost_attributed_signal(
    event_name: str,
    trace_id: str,
    revenue: float,
    *,
    customer_id: Optional[str] = None,
    session_id: Optional[str] = None,
    metadata: Optional[Dict[str, Any]] = None,
    currency: str = "USD",
    event_type: Optional[str] = None,
    order_id: Optional[str] = None,
    group_by: Optional[Dict[str, str]] = None,
    idempotency_key: Optional[str] = None,
    cost_data: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """
    Create a signal explicitly linked to an AI trace for cost attribution.
    
    This is the recommended way to link business events to AI costs.
    Requires trace_id from the AI call you want to attribute costs to.
    Sets enable_cost_tracing=True automatically.
    
    v8.1.0: Added as part of Signals Revamp Phase 2.
    
    Args:
        event_name: Name of the business event
        trace_id: Trace ID from the AI call to attribute costs to
        revenue: Revenue amount for this event
        customer_id: Customer ID (uses global config if not provided)
        session_id: Session ID for attribution
        metadata: Additional metadata
        currency: Currency (default: USD)
        event_type: Type of event for categorization
        order_id: Order ID to link this signal to
        group_by: Business grouping keys
        idempotency_key: Dedup key
        
    Returns:
        Dict with status and response data
        
    Example:
        >>> # After an AI call, link the revenue to its trace
        >>> cost_attributed_signal(
        ...     "purchase",
        ...     trace_id="abc123def456",
        ...     revenue=99.99,
        ...     metadata={"product_id": "prod-123"},
        ... )
    """
    config = get_signal_config()
    # Force enable_cost_tracing via config override
    config_with_cost = {**config, "enable_cost_tracing": True}
    return _emit_signal(
        event_name=event_name,
        config=config_with_cost,
        customer_id=customer_id or config.get("customer_id"),
        agent_id=config.get("agent_id"),
        trace_id=trace_id,
        revenue=revenue,
        currency=currency,
        event_type=event_type,
        session_id=session_id,
        order_id=order_id,
        group_by=group_by,
        idempotency_key=idempotency_key,
        metadata=metadata,
        cost_data=cost_data,
    )


def track_conversion(
    event_type: str,
    event_value: float,
    *,
    currency: str = "USD",
    session_id: Optional[str] = None,
    metadata: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
    """
    Track a conversion event for revenue attribution.
    
    Convenience wrapper around signal() for conversion tracking.
    
    Args:
        event_type: Type of conversion (e.g., "purchase", "signup", "subscription")
        event_value: Value of the conversion
        currency: Currency for the value
        session_id: Session ID for attribution window
        metadata: Additional conversion metadata
        
    Returns:
        Dict with status and response data
        
    Example:
        >>> track_conversion("purchase", 49.99, metadata={"plan": "pro"})
    """
    return signal(
        event_name=f"conversion_{event_type}",
        revenue=event_value,
        event_type=event_type,
        event_value=event_value,
        currency=currency,
        session_id=session_id,
        metadata=metadata,
    )
