"""
Organization settings service with Redis caching.

Manages per-org IAM enforcement configuration, including
the enforcement toggle, mode selection, and default policy setup.
"""

import json
import structlog
from typing import Optional, Dict, Any
from sqlalchemy.orm import Session

from control_plane_api.app.models.organization_settings import OrganizationSettings
from control_plane_api.app.lib.redis_client import get_redis_client

logger = structlog.get_logger()

CACHE_KEY_PREFIX = "org_settings:"
CACHE_TTL = 300  # 5 minutes


class SettingsService:
    """Service for managing organization-level IAM settings."""

    def __init__(self, organization_id: str, db: Session):
        self.organization_id = organization_id
        self.db = db

    async def get_settings(self) -> Optional[OrganizationSettings]:
        """Get settings for the current org, checking Redis cache first."""
        # Try cache
        cached = await self._get_cached()
        if cached:
            return cached

        settings = self.db.query(OrganizationSettings).filter(
            OrganizationSettings.organization_id == self.organization_id
        ).first()

        if settings:
            await self._set_cached(settings)

        return settings

    def upsert_settings(
        self,
        enforcement_enabled: Optional[bool] = None,
        enforcement_mode: Optional[str] = None,
        default_policy_id: Optional[str] = None,
    ) -> OrganizationSettings:
        """Create or update settings for the current org."""
        settings = self.db.query(OrganizationSettings).filter(
            OrganizationSettings.organization_id == self.organization_id
        ).first()

        if not settings:
            settings = OrganizationSettings(
                organization_id=self.organization_id,
                enforcement_enabled=enforcement_enabled if enforcement_enabled is not None else False,
                enforcement_mode=enforcement_mode or "monitoring",
            )
            self.db.add(settings)
        else:
            if enforcement_enabled is not None:
                settings.enforcement_enabled = enforcement_enabled
            if enforcement_mode is not None:
                settings.enforcement_mode = enforcement_mode

        if default_policy_id is not None:
            settings.default_policy_id = default_policy_id

        self.db.commit()
        self.db.refresh(settings)

        # Invalidate cache
        self._invalidate_cache_sync()

        logger.info(
            "organization_settings_upserted",
            organization_id=self.organization_id,
            enforcement_enabled=settings.enforcement_enabled,
            enforcement_mode=settings.enforcement_mode,
        )

        return settings

    async def _get_cached(self) -> Optional[OrganizationSettings]:
        """Get settings from Redis cache."""
        try:
            redis = get_redis_client()
            if not redis:
                return None

            key = f"{CACHE_KEY_PREFIX}{self.organization_id}"
            data = await redis.get(key)
            if data:
                parsed = json.loads(data) if isinstance(data, str) else data
                # Reconstruct model from cached data
                settings = OrganizationSettings(
                    organization_id=parsed["organization_id"],
                    enforcement_enabled=parsed["enforcement_enabled"],
                    enforcement_mode=parsed["enforcement_mode"],
                    default_policy_id=parsed.get("default_policy_id"),
                )
                return settings
        except Exception as e:
            logger.warning("settings_cache_get_failed", error=str(e))
        return None

    async def _set_cached(self, settings: OrganizationSettings) -> None:
        """Cache settings in Redis."""
        try:
            redis = get_redis_client()
            if not redis:
                return

            key = f"{CACHE_KEY_PREFIX}{self.organization_id}"
            data = json.dumps({
                "organization_id": settings.organization_id,
                "enforcement_enabled": settings.enforcement_enabled,
                "enforcement_mode": settings.enforcement_mode,
                "default_policy_id": settings.default_policy_id,
            })
            await redis.set(key, data, ex=CACHE_TTL)
        except Exception as e:
            logger.warning("settings_cache_set_failed", error=str(e))

    def _invalidate_cache_sync(self) -> None:
        """Invalidate cache (best-effort, non-blocking)."""
        try:
            import asyncio
            redis = get_redis_client()
            if redis:
                key = f"{CACHE_KEY_PREFIX}{self.organization_id}"
                try:
                    loop = asyncio.get_event_loop()
                    if loop.is_running():
                        loop.create_task(redis.delete(key))
                    else:
                        loop.run_until_complete(redis.delete(key))
                except RuntimeError:
                    pass  # No event loop, cache will expire naturally
        except Exception as e:
            logger.warning("settings_cache_invalidate_failed", error=str(e))
