# ================================================================
# Auth Service Inter-Service Communication Clients
# ================================================================
"""
Inter-service communication clients for the Auth Service to communicate
with other microservices in the bakery forecasting platform.

These clients handle authenticated API calls to:
- Tenant Service
- Training Service  
- Forecasting Service
- Notification Service
"""

import structlog
from typing import Dict, Any, Optional, List, Union
from shared.clients.base_service_client import BaseServiceClient
from shared.config.base import BaseServiceSettings

logger = structlog.get_logger()

# ================================================================
# TENANT SERVICE CLIENT
# ================================================================

class AuthTenantServiceClient(BaseServiceClient):
    """Client for Auth Service to communicate with Tenant Service"""
    
    def __init__(self, config: BaseServiceSettings):
        super().__init__("auth", config)
        self.service_url = config.TENANT_SERVICE_URL
    
    def get_service_base_path(self) -> str:
        return "/api/v1"
    
    # ================================================================
    # USER TENANT OPERATIONS
    # ================================================================
    
    async def get_user_tenants(self, user_id: str) -> Optional[List[Dict[str, Any]]]:
        """Get all tenant memberships for a user"""
        try:
            result = await self.get(f"tenants/user/{user_id}")
            return result.get("memberships", []) if result else []
        except Exception as e:
            logger.error(f"Failed to get user tenants: {str(e)}, user_id: {user_id}")
            return []
    
    async def get_user_owned_tenants(self, user_id: str) -> Optional[List[Dict[str, Any]]]:
        """Get tenants owned by a user"""
        try:
            memberships = await self.get_user_tenants(user_id)
            if memberships:
                return [m for m in memberships if m.get('role') == 'owner']
            return []
        except Exception as e:
            logger.error(f"Failed to get owned tenants: {str(e)}, user_id: {user_id}")
            return []
    
    async def transfer_tenant_ownership(
        self, 
        tenant_id: str, 
        current_owner_id: str, 
        new_owner_id: str
    ) -> Optional[Dict[str, Any]]:
        """Transfer tenant ownership from one user to another"""
        try:
            data = {
                "current_owner_id": current_owner_id,
                "new_owner_id": new_owner_id
            }
            return await self.post(f"tenants/{tenant_id}/transfer-ownership", data=data)
        except Exception as e:
            logger.error("Failed to transfer tenant ownership", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return None
    
    async def delete_tenant(self, tenant_id: str) -> Optional[Dict[str, Any]]:
        """Delete a tenant completely"""
        try:
            return await self.delete(f"tenants/{tenant_id}")
        except Exception as e:
            logger.error(f"Failed to delete tenant: {str(e)}, tenant_id: {tenant_id}")
            return None
    
    async def delete_user_memberships(self, user_id: str) -> Optional[Dict[str, Any]]:
        """Delete all tenant memberships for a user"""
        try:
            return await self.delete(f"/tenants/user/{user_id}/memberships")
        except Exception as e:
            logger.error(f"Failed to delete user memberships: {str(e)}, user_id: {user_id}")
            return None
    
    async def get_tenant_members(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
        """Get all members of a tenant"""
        try:
            result = await self.get(f"tenants/{tenant_id}/members")
            return result.get("members", []) if result else []
        except Exception as e:
            logger.error(f"Failed to get tenant members: {str(e)}, tenant_id: {tenant_id}")
            return []
    
    async def check_tenant_has_other_admins(self, tenant_id: str, excluding_user_id: str) -> bool:
        """Check if tenant has other admin users besides the excluded one"""
        try:
            members = await self.get_tenant_members(tenant_id)
            if members:
                admin_members = [
                    m for m in members 
                    if m.get('role') in ['admin', 'owner'] and m.get('user_id') != excluding_user_id
                ]
                return len(admin_members) > 0
            return False
        except Exception as e:
            logger.error("Failed to check tenant admins", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return False

# ================================================================
# TRAINING SERVICE CLIENT
# ================================================================

class AuthTrainingServiceClient(BaseServiceClient):
    """Client for Auth Service to communicate with Training Service"""
    
    def __init__(self, config: BaseServiceSettings):
        super().__init__("auth", config)
        self.service_url = config.TRAINING_SERVICE_URL
    
    def get_service_base_path(self) -> str:
        return "/api/v1"
    
    # ================================================================
    # TRAINING JOB OPERATIONS
    # ================================================================
    
    async def cancel_tenant_training_jobs(self, tenant_id: str) -> Optional[Dict[str, Any]]:
        """Cancel all active training jobs for a tenant"""
        try:
            data = {"tenant_id": tenant_id}
            return await self.post("/tenants/{tenant_id}/training/jobs/cancel", data=data)
        except Exception as e:
            logger.error("Failed to cancel tenant training jobs", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return {"jobs_cancelled": 0, "errors": [str(e)]}
    
    async def get_tenant_active_jobs(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
        """Get all active training jobs for a tenant"""
        try:
            params = {"status": "running,queued,pending", "tenant_id": tenant_id}
            result = await self.get("/tenants/{tenant_id}/training/jobs/active", params=params)
            return result.get("jobs", []) if result else []
        except Exception as e:
            logger.error("Failed to get tenant active jobs", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return []
    
    # ================================================================
    # MODEL OPERATIONS
    # ================================================================
    
    async def delete_tenant_models(self, tenant_id: str) -> Optional[Dict[str, Any]]:
        """Delete all trained models and artifacts for a tenant"""
        try:
            return await self.delete(f"models/tenant/{tenant_id}")
        except Exception as e:
            logger.error("Failed to delete tenant models", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return {"models_deleted": 0, "artifacts_deleted": 0, "errors": [str(e)]}
    
    async def get_tenant_models_count(self, tenant_id: str) -> int:
        """Get count of trained models for a tenant"""
        try:
            result = await self.get(f"models/tenant/{tenant_id}/count")
            return result.get("count", 0) if result else 0
        except Exception as e:
            logger.error("Failed to get tenant models count", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return 0
    
    async def get_tenant_model_artifacts(self, tenant_id: str) -> Optional[List[Dict[str, Any]]]:
        """Get all model artifacts for a tenant"""
        try:
            result = await self.get(f"models/tenant/{tenant_id}/artifacts")
            return result.get("artifacts", []) if result else []
        except Exception as e:
            logger.error("Failed to get tenant model artifacts", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return []

# ================================================================
# FORECASTING SERVICE CLIENT
# ================================================================

class AuthForecastingServiceClient(BaseServiceClient):
    """Client for Auth Service to communicate with Forecasting Service"""
    
    def __init__(self, config: BaseServiceSettings):
        super().__init__("auth", config)
        self.service_url = config.FORECASTING_SERVICE_URL
    
    def get_service_base_path(self) -> str:
        return "/api/v1"
    
    # ================================================================
    # FORECAST OPERATIONS
    # ================================================================
    
    async def delete_tenant_forecasts(self, tenant_id: str) -> Optional[Dict[str, Any]]:
        """Delete all forecasts and predictions for a tenant"""
        try:
            return await self.delete(f"forecasts/tenant/{tenant_id}")
        except Exception as e:
            logger.error("Failed to delete tenant forecasts", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return {
                "forecasts_deleted": 0, 
                "predictions_deleted": 0, 
                "cache_cleared": 0,
                "errors": [str(e)]
            }
    
    async def get_tenant_forecasts_count(self, tenant_id: str) -> int:
        """Get count of forecasts for a tenant"""
        try:
            result = await self.get(f"forecasts/tenant/{tenant_id}/count")
            return result.get("count", 0) if result else 0
        except Exception as e:
            logger.error("Failed to get tenant forecasts count", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return 0
    
    async def clear_tenant_prediction_cache(self, tenant_id: str) -> Optional[Dict[str, Any]]:
        """Clear prediction cache for a tenant"""
        try:
            return await self.post(f"predictions/cache/clear/{tenant_id}")
        except Exception as e:
            logger.error("Failed to clear tenant prediction cache", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return {"cache_cleared": 0, "errors": [str(e)]}
    
    async def cancel_tenant_prediction_batches(self, tenant_id: str) -> Optional[Dict[str, Any]]:
        """Cancel any active prediction batches for a tenant"""
        try:
            data = {"tenant_id": tenant_id}
            return await self.post("predictions/batches/cancel", data=data)
        except Exception as e:
            logger.error("Failed to cancel tenant prediction batches", 
                        tenant_id=tenant_id, 
                        error=str(e))
            return {"batches_cancelled": 0, "errors": [str(e)]}

# ================================================================
# NOTIFICATION SERVICE CLIENT
# ================================================================

class AuthNotificationServiceClient(BaseServiceClient):
    """Client for Auth Service to communicate with Notification Service"""
    
    def __init__(self, config: BaseServiceSettings):
        super().__init__("auth", config)
        self.service_url = config.NOTIFICATION_SERVICE_URL
    
    def get_service_base_path(self) -> str:
        return "/api/v1"
    
    # ================================================================
    # USER NOTIFICATION OPERATIONS
    # ================================================================
    
    async def delete_user_notification_data(self, user_id: str) -> Optional[Dict[str, Any]]:
        """Delete all notification data for a user"""
        try:
            return await self.delete(f"/users/{user_id}/notification-data")
        except Exception as e:
            logger.error("Failed to delete user notification data", 
                        user_id=user_id, 
                        error=str(e))
            return {
                "preferences_deleted": 0,
                "notifications_deleted": 0,
                "logs_deleted": 0,
                "errors": [str(e)]
            }
    
    async def cancel_pending_user_notifications(self, user_id: str) -> Optional[Dict[str, Any]]:
        """Cancel all pending notifications for a user"""
        try:
            return await self.post(f"users/{user_id}/notifications/cancel-pending")
        except Exception as e:
            logger.error("Failed to cancel user notifications", 
                        user_id=user_id, 
                        error=str(e))
            return {"notifications_cancelled": 0, "errors": [str(e)]}
    
    async def get_user_notification_count(self, user_id: str) -> int:
        """Get count of notifications for a user"""
        try:
            result = await self.get(f"users/{user_id}/notifications/count")
            return result.get("count", 0) if result else 0
        except Exception as e:
            logger.error("Failed to get user notification count", 
                        user_id=user_id, 
                        error=str(e))
            return 0
    
    async def send_user_deletion_notification(
        self, 
        admin_email: str, 
        deleted_user_email: str,
        deletion_summary: Dict[str, Any]
    ) -> Optional[Dict[str, Any]]:
        """Send notification about user deletion to administrators"""
        try:
            data = {
                "type": "email",
                "recipient_email": admin_email,
                "template_key": "user_deletion_notification",
                "template_data": {
                    "deleted_user_email": deleted_user_email,
                    "deletion_summary": deletion_summary
                },
                "priority": "high"
            }
            return await self.post("notifications/send", data=data)
        except Exception as e:
            logger.error("Failed to send user deletion notification", 
                        admin_email=admin_email, 
                        error=str(e))
            return None

# ================================================================
# CLIENT FACTORY
# ================================================================

class AuthServiceClientFactory:
    """Factory for creating inter-service clients for Auth Service"""
    
    def __init__(self, config: BaseServiceSettings):
        self.config = config
        self._tenant_client = None
        self._training_client = None
        self._forecasting_client = None
        self._notification_client = None
    
    @property
    def tenant_client(self) -> AuthTenantServiceClient:
        """Get or create tenant service client"""
        if self._tenant_client is None:
            self._tenant_client = AuthTenantServiceClient(self.config)
        return self._tenant_client
    
    @property
    def training_client(self) -> AuthTrainingServiceClient:
        """Get or create training service client"""
        if self._training_client is None:
            self._training_client = AuthTrainingServiceClient(self.config)
        return self._training_client
    
    @property
    def forecasting_client(self) -> AuthForecastingServiceClient:
        """Get or create forecasting service client"""
        if self._forecasting_client is None:
            self._forecasting_client = AuthForecastingServiceClient(self.config)
        return self._forecasting_client
    
    @property
    def notification_client(self) -> AuthNotificationServiceClient:
        """Get or create notification service client"""
        if self._notification_client is None:
            self._notification_client = AuthNotificationServiceClient(self.config)
        return self._notification_client
    
    async def health_check_all_services(self) -> Dict[str, bool]:
        """Check health of all services"""
        results = {}
        
        clients = [
            ("tenant", self.tenant_client),
            ("training", self.training_client),
            ("forecasting", self.forecasting_client),
            ("notification", self.notification_client)
        ]
        
        for service_name, client in clients:
            try:
                health = await client.get("health")
                results[service_name] = health is not None and health.get("status") == "healthy"
            except Exception as e:
                logger.error(f"Health check failed for {service_name} service", error=str(e))
                results[service_name] = False
        
        return results