"""
Briefcase AI  The Universal Audit Plane for AI Engineering.

This module provides a unified interface for Execution, Tracing, Evaluation, and Lineage.
"""

import os
import atexit
from typing import Optional, Any, Callable, List, Dict, Union

# Core compiled bindings
from ._core import *  # noqa: F401, F403
from ._core import __version__, __author__, init as _core_init, init_with_config as _core_init_with_config, _shutdown_runtime as _core_shutdown_runtime

# Aliases for better DX
Decision = DecisionSnapshot

#  Pillar 1: Universal Execution (bf.Model) 

class Model:
    """
    Universal interface for LLM execution across providers.
    Automatically captures traces, costs, and latency.
    """
    
    # Model Resiliency: Mapping deprecated versions to latest
    ALIAS_MAP = {
        "claude-3-5-sonnet": "claude-3-7-sonnet-latest",
        "claude-3-5-haiku": "claude-3-7-haiku-latest",
        "gpt-4": "gpt-4o",
        "gemini-1.0-pro": "gemini-1.5-pro",
        "gemini-pro": "gemini-1.5-pro",
        "gemini-ultra": "gemini-1.5-pro",
    }

    def __init__(self, model_id: str, provider: str = "auto", **kwargs):
        self.model_id = self.ALIAS_MAP.get(model_id, model_id)
        self.provider = self._resolve_provider(provider, self.model_id)
        self.client = self._init_client(self.provider, **kwargs)

    def _resolve_provider(self, provider: str, model_id: str) -> str:
        if provider != "auto":
            return provider
        if "gpt" in model_id: return "openai"
        if "claude" in model_id: return "anthropic"
        if "gemini" in model_id: return "gemini"
        if os.getenv("TOGETHER_API_KEY"): return "together"
        return "local"

    def _init_client(self, provider: str, **kwargs):
        if provider == "openai":
            from .providers.openai import OpenAIProvider
            return OpenAIProvider(self.model_id, **kwargs)
        elif provider == "anthropic":
            from .providers.anthropic import AnthropicProvider
            return AnthropicProvider(self.model_id, **kwargs)
        elif provider == "gemini":
            from .providers.gemini import GeminiProvider
            return GeminiProvider(self.model_id, **kwargs)
        elif provider == "together":
            from .providers.together import TogetherProvider
            return TogetherProvider(self.model_id, **kwargs)
        elif provider == "sglang":
            from .providers.sglang import SGLangProvider
            return SGLangProvider(self.model_id, **kwargs)
        else:
            from .providers.local import LocalProvider
            return LocalProvider(self.model_id, **kwargs)

    def generate(self, prompt: str, **kwargs) -> Any:
        return self.client.generate(prompt, **kwargs)

#  Pillar 2: Universal Tracing (bf.observe) 

class observe:
    """
    Context manager and decorator to auto-instrument functions as Decisions.
    """
    def __init__(self, name: Optional[str] = None):
        self.name = name
        self.decision = None

    def __enter__(self):
        self.decision = Decision(self.name or "observation")
        return self.decision

    def __exit__(self, exc_type, exc_val, exc_tb):
        if exc_type:
            self.decision.with_error(str(exc_val), exc_type.__name__)
        save(self.decision)

    def __call__(self, func):
        import functools
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            with observe(self.name or func.__name__):
                return func(*args, **kwargs)
        return wrapper

#  Unified Initialization & Persistence 

_GLOBAL_BACKEND = None

def init(mode: str = "standard", backend: str = "sqlite", **kwargs):
    """
    Initialize the Universal Audit Plane.
    
    Args:
        mode: "standard" or "performance" (async).
        backend: "sqlite" or "lakefs".
    """
    global _GLOBAL_BACKEND
    
    # 1. Core Rust init
    _core_init_with_config(worker_threads=kwargs.get("threads", 4))
    
    # 2. Resolve Base Backend
    if backend == "sqlite":
        base_backend = SqliteBackend(kwargs.get("path", "decisions.db"))
    elif backend == "lakefs":
        base_backend = LakeFSBackend(
            endpoint=kwargs.get("endpoint", os.getenv("LAKEFS_ENDPOINT_URL")),
            repository=kwargs.get("repository", os.getenv("LAKEFS_REPOSITORY")),
            branch=kwargs.get("branch", "main"),
            access_key=kwargs.get("access_key", os.getenv("LAKEFS_ACCESS_KEY_ID")),
            secret_key=kwargs.get("secret_key", os.getenv("LAKEFS_SECRET_ACCESS_KEY"))
        )
    else:
        base_backend = SqliteBackend.in_memory()

    # 3. Apply Performance Mode (Buffering)
    if mode == "performance":
        _GLOBAL_BACKEND = BufferedBackend(base_backend, buffer_size=kwargs.get("buffer_size", 1000))
    else:
        _GLOBAL_BACKEND = base_backend
    
def save(decision: Decision):
    """Global save helper."""
    if _GLOBAL_BACKEND:
        _GLOBAL_BACKEND.save_decision(decision)

def shutdown():
    _core_shutdown_runtime()

atexit.register(shutdown)

# Lazy submodule imports — defer until first attribute access to break
# circular-import chains (e.g. env/generators/factory.py does
# `import briefcase_ai as bf` before this module is fully initialised).
def __getattr__(name: str):
    import importlib
    _lazy_modules = {"harness", "env", "providers", "data"}
    if name in _lazy_modules:
        mod = importlib.import_module(f".{name}", __name__)
        globals()[name] = mod
        return mod
    if name == "detect_hardware":
        from .hardware import detect_hardware as _dh
        globals()["detect_hardware"] = _dh
        return _dh
    if name == "Experiment":
        import importlib as _il
        _h = _il.import_module(".harness", __name__)
        globals()["harness"] = _h
        globals()["Experiment"] = _h.Experiment
        return _h.Experiment
    raise AttributeError(f"module {__name__!r} has no attribute {name!r}")

# Compatibility shims: forward to briefcase.integrations.lakefs with deprecation warning
def _make_lakefs_shim(name: str):
    import warnings
    import functools
    def shim(*args, **kwargs):
        warnings.warn(
            f"briefcase_ai.{name} is deprecated; use briefcase.integrations.lakefs.{name}",
            DeprecationWarning,
            stacklevel=2,
        )
        import importlib
        mod = importlib.import_module("briefcase.integrations.lakefs")
        return getattr(mod, name)(*args, **kwargs)
    shim.__name__ = name
    shim.__qualname__ = name
    return shim

versioned = _make_lakefs_shim("versioned")
lakefs_versioned = _make_lakefs_shim("lakefs_versioned")
versioned_context = _make_lakefs_shim("versioned_context")
lakefs_context = _make_lakefs_shim("lakefs_context")
def briefcase_workflow(*args, **kwargs):
    import warnings
    warnings.warn(
        "briefcase_ai.briefcase_workflow is deprecated; use briefcase.correlation.briefcase_workflow",
        DeprecationWarning,
        stacklevel=2,
    )
    from briefcase.correlation import briefcase_workflow as _bfw
    return _bfw(*args, **kwargs)


__all__ = [
    "init", "shutdown", "save", "Model", "observe", "Scorecard", "Experiment",
    "Decision", "DataRef", "Input", "Output", "ModelParameters",
    "SqliteBackend", "LakeFSBackend", "BufferedBackend", "harness", "env", "providers", "data", "detect_hardware",
    "versioned", "lakefs_versioned", "versioned_context", "lakefs_context", "briefcase_workflow",
]
