import fnmatch
import os
import redis

redis_client = None
rag_redis_client = None
sales_context_factory = None
sap_api_service = None
conversation_store = None
daily_digest_service = None
digest_scheduler = None
error_logger = None
rbac_service = None
limiter = None

class RedisFallback:
    def __init__(self, host="localhost", port=6379, db=0, decode=True):
        self._cache: dict = {}
        self.enabled = False
        try:
            self.client = redis.StrictRedis(host=host, port=port, db=db, decode_responses=decode, socket_connect_timeout=1)
            self.client.ping()
            self.enabled = True
            print(f"[Redis] Connected at {host}:{port}")
        except Exception as e:
            print(f"[Redis] Unavailable ({e}). Using in-memory fallback.")

    def get(self, key):
        if self.enabled:
            try: return self.client.get(key)
            except Exception: pass
        return self._cache.get(key)

    def set(self, key, value, ex=None):
        if self.enabled:
            try: return self.client.set(key, value, ex=ex)
            except Exception: pass
        self._cache[key] = value
        return True

    def delete(self, key):
        if self.enabled:
            try: return self.client.delete(key)
            except Exception: pass
        self._cache.pop(key, None)

    def keys(self, pattern):
        if self.enabled:
            try: return self.client.keys(pattern)
            except Exception: pass
        return [k for k in self._cache if fnmatch.fnmatch(k, pattern)]

    def scan_iter(self, pattern):
        if self.enabled:
            try: return self.client.scan_iter(pattern)
            except Exception: pass
        return iter([k for k in self._cache if fnmatch.fnmatch(k, pattern)])

    def exists(self, key):
        if self.enabled:
            try: return self.client.exists(key)
            except Exception: pass
        return key in self._cache

    def expire(self, key, seconds):
        if self.enabled:
            try: return self.client.expire(key, seconds)
            except Exception: pass
        return True

    def hset(self, name, mapping=None, **kwargs):
        if self.enabled:
            try: return self.client.hset(name, mapping=mapping, **kwargs)
            except Exception: pass
        if name not in self._cache: self._cache[name] = {}
        if mapping: self._cache[name].update(mapping)
        self._cache[name].update(kwargs)

    def hgetall(self, name):
        if self.enabled:
            try: return self.client.hgetall(name)
            except Exception: pass
        return self._cache.get(name, {})


def init_extensions(app, config) -> None:
    global redis_client, rag_redis_client, sales_context_factory
    global sap_api_service, conversation_store, daily_digest_service
    global digest_scheduler, error_logger, rbac_service, limiter

    from flask_limiter import Limiter
    from flask_limiter.util import get_remote_address
    from services.conversation_store import ConversationStore
    from services.daily_digest_service import DailyDigestService
    from services.digest_scheduler import DigestScheduler
    from services.sap_api_service import SapApiService
    from services.error_logger import StructuredErrorLogger

    redis_client     = RedisFallback(host=config.REDIS_HOST, port=config.REDIS_PORT, db=config.REDIS_DB, decode=True)
    rag_redis_client = RedisFallback(host=config.REDIS_HOST, port=config.REDIS_PORT, db=config.REDIS_DB, decode=False)

    os.makedirs(config.SALES_DB_ROOT, exist_ok=True)
    sales_context_factory = True  # placeholder — HANA used directly

    sap_api_service   = SapApiService()
    conversation_store = ConversationStore(redis_client)

    def _hana_factory():
        try:
            import hdbcli.dbapi as hdbcli
            return hdbcli.connect(
                address=os.getenv("HANA_HOST", "localhost"),
                port=int(os.getenv("HANA_PORT", "30215")),
                user=os.getenv("HANA_USER", ""),
                password=os.getenv("HANA_PASSWORD", ""),
            )
        except Exception as exc:
            raise RuntimeError(f"HANA connect failed: {exc}") from exc

    daily_digest_service = DailyDigestService(redis_client, hana_conn_factory=_hana_factory)
    digest_recipients    = [e.strip() for e in os.getenv("DIGEST_RECIPIENTS", "").split(",") if e.strip()]
    digest_scheduler     = DigestScheduler(digest_service=daily_digest_service, redis_client=redis_client, recipients=digest_recipients)

    limiter = Limiter(key_func=get_remote_address, app=app, storage_uri=f"redis://{config.REDIS_HOST}:{config.REDIS_PORT}", default_limits=[])

    import services.rbac_service as _rbac
    rbac_db = getattr(config, 'RBAC_DB', None) or os.path.join(app.root_path, 'data', 'rbac.db')
    _rbac.init_rbac(db_path=rbac_db)
    rbac_service = _rbac

    log_file   = os.path.join(app.root_path, "logs", "apex_errors.log")
    error_logger = StructuredErrorLogger(log_file=log_file)