# ================================================================
# Admin User Delete API - Complete Implementation
# ================================================================
"""
Complete admin user deletion API that handles all associated data
across all microservices in the bakery forecasting platform.

This implementation ensures proper cascade deletion of:
1. User account and authentication data
2. Tenant ownership and memberships
3. All training models and artifacts
4. Forecasts and predictions
5. Notification preferences and logs
6. Refresh tokens and sessions
"""

from fastapi import APIRouter, Depends, HTTPException, status, BackgroundTasks
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, delete, text
from typing import Dict, List, Any, Optional
import structlog
import uuid
from datetime import datetime

from shared.auth.decorators import get_current_user_dep
from app.core.database import get_db
from app.services.auth_service_clients import AuthServiceClientFactory
from app.core.config import settings

logger = structlog.get_logger()

router = APIRouter()

class AdminUserDeleteService:
    """Service to handle complete admin user deletion across all microservices"""
    
    def __init__(self, db: AsyncSession, event_publisher=None):
        self.db = db
        self.clients = AuthServiceClientFactory(settings)
        self.event_publisher = event_publisher
        
    async def delete_admin_user_complete(self, user_id: str, requesting_user_id: str) -> Dict[str, Any]:
        """
        Complete admin user deletion with all associated data using inter-service clients
        
        Args:
            user_id: ID of the admin user to delete
            requesting_user_id: ID of the user performing the deletion
            
        Returns:
            Dictionary with deletion results from all services
        """
        
        deletion_results = {
            'user_id': user_id,
            'requested_by': requesting_user_id,
            'started_at': datetime.utcnow().isoformat(),
            'services_processed': {},
            'errors': [],
            'summary': {}
        }
        
        try:
            # Step 1: Validate user exists and is admin
            user_info = await self._validate_admin_user(user_id)
            if not user_info:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Admin user {user_id} not found"
                )
            
            deletion_results['user_info'] = user_info
            
            # Step 2: Get all tenant associations using tenant client
            tenant_info = await self._get_user_tenant_info(user_id)
            deletion_results['tenant_associations'] = tenant_info
            
            # Step 3: Delete in proper order to respect dependencies
            
            # 3.1 Stop all active training jobs and delete models
            training_result = await self._delete_training_data(tenant_info['tenant_ids'])
            deletion_results['services_processed']['training'] = training_result
            
            # 3.2 Delete all forecasts and predictions
            forecasting_result = await self._delete_forecasting_data(tenant_info['tenant_ids'])
            deletion_results['services_processed']['forecasting'] = forecasting_result
            
            # 3.3 Delete notification preferences and logs
            notification_result = await self._delete_notification_data(user_id)
            deletion_results['services_processed']['notification'] = notification_result
            
            # 3.4 Delete tenant memberships and handle owned tenants
            tenant_result = await self._delete_tenant_data(user_id, tenant_info)
            deletion_results['services_processed']['tenant'] = tenant_result
            
            # 3.5 Finally delete user account and auth data
            auth_result = await self._delete_auth_data(user_id)
            deletion_results['services_processed']['auth'] = auth_result
            
            # Step 4: Generate summary
            deletion_results['summary'] = await self._generate_deletion_summary(deletion_results)
            deletion_results['completed_at'] = datetime.utcnow().isoformat()
            deletion_results['status'] = 'success'
            
            # Step 5: Publish deletion event
            await self._publish_user_deleted_event(user_id, deletion_results)
            
            # Step 6: Send notification to admins
            await self._notify_admins_of_deletion(user_info, deletion_results)
            
            logger.info("Admin user deletion completed successfully", 
                       user_id=user_id, 
                       tenants_affected=len(tenant_info['tenant_ids']))
            
            return deletion_results
            
        except Exception as e:
            deletion_results['status'] = 'failed'
            deletion_results['error'] = str(e)
            deletion_results['completed_at'] = datetime.utcnow().isoformat()
            
            logger.error("Admin user deletion failed", 
                        user_id=user_id, 
                        error=str(e))
            
            # Attempt to publish failure event
            try:
                await self._publish_user_deletion_failed_event(user_id, str(e))
            except:
                pass
                
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail=f"User deletion failed: {str(e)}"
            )
    
    async def _validate_admin_user(self, user_id: str) -> Optional[Dict[str, Any]]:
        """Validate user exists and get basic info from local database"""
        try:
            from app.models.users import User
            from app.models.tokens import RefreshToken

            # Query user from local auth database
            query = select(User).where(User.id == uuid.UUID(user_id))
            result = await self.db.execute(query)
            user = result.scalar_one_or_none()
            
            if not user:
                return None
                
            return {
                'id': str(user.id),
                'email': user.email,
                'full_name': user.full_name,
                'created_at': user.created_at.isoformat() if user.created_at else None,
                'is_active': user.is_active,
                'is_verified': user.is_verified
            }
                
        except Exception as e:
            logger.error("Failed to validate admin user", user_id=user_id, error=str(e))
            raise
    
    async def _get_user_tenant_info(self, user_id: str) -> Dict[str, Any]:
        """Get all tenant associations for the user using tenant client"""
        try:
            # Use tenant service client to get memberships
            memberships = await self.clients.tenant_client.get_user_tenants(user_id)
            
            if not memberships:
                return {
                    'tenant_ids': [],
                    'total_tenants': 0,
                    'owned_tenants': 0,
                    'memberships': []
                }
            
            tenant_ids = [m['tenant_id'] for m in memberships]
            owned_tenants = [m for m in memberships if m.get('role') == 'owner']
            
            return {
                'tenant_ids': tenant_ids,
                'total_tenants': len(tenant_ids),
                'owned_tenants': len(owned_tenants),
                'memberships': memberships
            }
                
        except Exception as e:
            logger.error("Failed to get tenant info", user_id=user_id, error=str(e))
            return {'tenant_ids': [], 'total_tenants': 0, 'owned_tenants': 0, 'memberships': []}
    
    async def _delete_training_data(self, tenant_ids: List[str]) -> Dict[str, Any]:
        """Delete all training models, jobs, and artifacts for user's tenants"""
        result = {
            'models_deleted': 0,
            'jobs_cancelled': 0,
            'artifacts_deleted': 0,
            'total_tenants_processed': 0,
            'errors': []
        }
        
        try:
            for tenant_id in tenant_ids:
                try:
                    # Cancel active training jobs using training client
                    cancel_result = await self.clients.training_client.cancel_tenant_training_jobs(tenant_id)
                    if cancel_result:
                        result['jobs_cancelled'] += cancel_result.get('jobs_cancelled', 0)
                        if cancel_result.get('errors'):
                            result['errors'].extend(cancel_result['errors'])
                    
                    # Delete all models and artifacts using training client
                    delete_result = await self.clients.training_client.delete_tenant_models(tenant_id)
                    if delete_result:
                        result['models_deleted'] += delete_result.get('models_deleted', 0)
                        result['artifacts_deleted'] += delete_result.get('artifacts_deleted', 0)
                        if delete_result.get('errors'):
                            result['errors'].extend(delete_result['errors'])
                    
                    result['total_tenants_processed'] += 1
                    
                    logger.debug("Training data deleted for tenant", 
                               tenant_id=tenant_id,
                               models=delete_result.get('models_deleted', 0) if delete_result else 0)
                            
                except Exception as e:
                    error_msg = f"Error deleting training data for tenant {tenant_id}: {str(e)}"
                    result['errors'].append(error_msg)
                    logger.error(error_msg)
                        
        except Exception as e:
            result['errors'].append(f"Training service communication error: {str(e)}")
            
        return result
    
    async def _delete_forecasting_data(self, tenant_ids: List[str]) -> Dict[str, Any]:
        """Delete all forecasts, predictions, and caches for user's tenants"""
        result = {
            'forecasts_deleted': 0,
            'predictions_deleted': 0,
            'cache_cleared': 0,
            'batches_cancelled': 0,
            'total_tenants_processed': 0,
            'errors': []
        }
        
        try:
            for tenant_id in tenant_ids:
                try:
                    # Cancel any active prediction batches
                    batch_result = await self.clients.forecasting_client.cancel_tenant_prediction_batches(tenant_id)
                    if batch_result:
                        result['batches_cancelled'] += batch_result.get('batches_cancelled', 0)
                        if batch_result.get('errors'):
                            result['errors'].extend(batch_result['errors'])
                    
                    # Clear prediction cache
                    cache_result = await self.clients.forecasting_client.clear_tenant_prediction_cache(tenant_id)
                    if cache_result:
                        result['cache_cleared'] += cache_result.get('cache_cleared', 0)
                        if cache_result.get('errors'):
                            result['errors'].extend(cache_result['errors'])
                    
                    # Delete all forecasts for tenant
                    delete_result = await self.clients.forecasting_client.delete_tenant_forecasts(tenant_id)
                    if delete_result:
                        result['forecasts_deleted'] += delete_result.get('forecasts_deleted', 0)
                        result['predictions_deleted'] += delete_result.get('predictions_deleted', 0)
                        if delete_result.get('errors'):
                            result['errors'].extend(delete_result['errors'])
                    
                    result['total_tenants_processed'] += 1
                    
                    logger.debug("Forecasting data deleted for tenant", 
                               tenant_id=tenant_id,
                               forecasts=delete_result.get('forecasts_deleted', 0) if delete_result else 0)
                            
                except Exception as e:
                    error_msg = f"Error deleting forecasting data for tenant {tenant_id}: {str(e)}"
                    result['errors'].append(error_msg)
                    logger.error(error_msg)
                        
        except Exception as e:
            result['errors'].append(f"Forecasting service communication error: {str(e)}")
            
        return result
    
    async def _delete_notification_data(self, user_id: str) -> Dict[str, Any]:
        """Delete notification preferences, logs, and pending notifications"""
        result = {
            'preferences_deleted': 0,
            'notifications_deleted': 0,
            'notifications_cancelled': 0,
            'logs_deleted': 0,
            'errors': []
        }
        
        try:
            # Cancel pending notifications first
            cancel_result = await self.clients.notification_client.cancel_pending_user_notifications(user_id)
            if cancel_result:
                result['notifications_cancelled'] = cancel_result.get('notifications_cancelled', 0)
                if cancel_result.get('errors'):
                    result['errors'].extend(cancel_result['errors'])
            
            # Delete all notification data for user
            delete_result = await self.clients.notification_client.delete_user_notification_data(user_id)
            if delete_result:
                result['preferences_deleted'] = delete_result.get('preferences_deleted', 0)
                result['notifications_deleted'] = delete_result.get('notifications_deleted', 0)
                result['logs_deleted'] = delete_result.get('logs_deleted', 0)
                if delete_result.get('errors'):
                    result['errors'].extend(delete_result['errors'])
            
            logger.debug("Notification data deleted for user", 
                        user_id=user_id,
                        notifications=result['notifications_deleted'])
                    
        except Exception as e:
            result['errors'].append(f"Notification service communication error: {str(e)}")
            
        return result
    
    async def _delete_tenant_data(self, user_id: str, tenant_info: Dict[str, Any]) -> Dict[str, Any]:
        """Delete tenant memberships and handle owned tenants using tenant client"""
        result = {
            'memberships_deleted': 0,
            'tenants_deleted': 0,
            'tenants_transferred': 0,
            'errors': []
        }
        
        try:
            # Handle owned tenants - either delete or transfer ownership
            for membership in tenant_info['memberships']:
                if membership.get('role') == 'owner':
                    tenant_id = membership['tenant_id']
                    
                    try:
                        # Check if tenant has other admin members who can take ownership
                        has_other_admins = await self.clients.tenant_client.check_tenant_has_other_admins(
                            tenant_id, user_id
                        )
                        
                        if has_other_admins:
                            # Get tenant members to find first admin
                            members = await self.clients.tenant_client.get_tenant_members(tenant_id)
                            admin_members = [
                                m for m in members 
                                if m.get('role') == 'admin' and m.get('user_id') != user_id
                            ]
                            
                            if admin_members:
                                # Transfer ownership to first admin
                                transfer_result = await self.clients.tenant_client.transfer_tenant_ownership(
                                    tenant_id, user_id, admin_members[0]['user_id']
                                )
                                
                                if transfer_result:
                                    result['tenants_transferred'] += 1
                                    logger.info("Transferred tenant ownership", 
                                              tenant_id=tenant_id,
                                              new_owner=admin_members[0]['user_id'])
                                else:
                                    result['errors'].append(f"Failed to transfer ownership of tenant {tenant_id}")
                            else:
                                result['errors'].append(f"No admin members found for tenant {tenant_id}")
                        else:
                            # No other admins, delete the tenant completely
                            delete_result = await self.clients.tenant_client.delete_tenant(tenant_id)
                            
                            if delete_result:
                                result['tenants_deleted'] += 1
                                logger.info("Deleted tenant", tenant_id=tenant_id)
                            else:
                                result['errors'].append(f"Failed to delete tenant {tenant_id}")
                                
                    except Exception as e:
                        error_msg = f"Error handling owned tenant {tenant_id}: {str(e)}"
                        result['errors'].append(error_msg)
                        logger.error(error_msg)
            
            # Delete user's memberships
            delete_result = await self.clients.tenant_client.delete_user_memberships(user_id)
            if delete_result:
                result['memberships_deleted'] = delete_result.get('memberships_deleted', 0)
                if delete_result.get('errors'):
                    result['errors'].extend(delete_result['errors'])
            else:
                result['errors'].append("Failed to delete user memberships")
                    
        except Exception as e:
            result['errors'].append(f"Tenant service communication error: {str(e)}")
            
        return result
    
    async def _delete_auth_data(self, user_id: str) -> Dict[str, Any]:
        """Delete user account, refresh tokens, and auth data from local database"""
        result = {
            'user_deleted': False,
            'refresh_tokens_deleted': 0,
            'sessions_invalidated': 0,
            'errors': []
        }
        
        try:
            from app.models.users import User
            from app.models.tokens import RefreshToken

            # Delete refresh tokens
            token_delete_query = delete(RefreshToken).where(RefreshToken.user_id == uuid.UUID(user_id))
            token_result = await self.db.execute(token_delete_query)
            result['refresh_tokens_deleted'] = token_result.rowcount
            
            # Delete user account
            user_delete_query = delete(User).where(User.id == uuid.UUID(user_id))
            user_result = await self.db.execute(user_delete_query)
            
            if user_result.rowcount > 0:
                result['user_deleted'] = True
                await self.db.commit()
                logger.info("User and tokens deleted from auth database", 
                          user_id=user_id,
                          tokens_deleted=result['refresh_tokens_deleted'])
            else:
                result['errors'].append("User not found in auth database")
                await self.db.rollback()
                    
        except Exception as e:
            await self.db.rollback()
            error_msg = f"Auth database error: {str(e)}"
            result['errors'].append(error_msg)
            logger.error(error_msg)
            
        return result
    
    async def _generate_deletion_summary(self, deletion_results: Dict[str, Any]) -> Dict[str, Any]:
        """Generate summary of deletion operation"""
        summary = {
            'total_tenants_affected': deletion_results['tenant_associations']['total_tenants'],
            'total_models_deleted': deletion_results['services_processed']['training']['models_deleted'],
            'total_forecasts_deleted': deletion_results['services_processed']['forecasting']['forecasts_deleted'],
            'total_notifications_deleted': deletion_results['services_processed']['notification']['notifications_deleted'],
            'tenants_transferred': deletion_results['services_processed']['tenant']['tenants_transferred'],
            'tenants_deleted': deletion_results['services_processed']['tenant']['tenants_deleted'],
            'user_deleted': deletion_results['services_processed']['auth']['user_deleted'],
            'total_errors': 0
        }
        
        # Count total errors across all services
        for service_result in deletion_results['services_processed'].values():
            if isinstance(service_result, dict) and 'errors' in service_result:
                summary['total_errors'] += len(service_result['errors'])
        
        # Add success indicator
        summary['deletion_successful'] = (
            summary['user_deleted'] and 
            summary['total_errors'] == 0
        )
        
        return summary
    


    async def _publish_user_deleted_event(self, user_id: str, deletion_results: Dict[str, Any]):
        """Publish user deletion event to message queue"""
        if self.event_publisher:
            try:
                await self.event_publisher.publish_business_event(
                    event_type="auth.user.deleted",
                    tenant_id="system",
                    data={
                        "user_id": user_id,
                        "timestamp": datetime.utcnow().isoformat(),
                        "deletion_summary": deletion_results['summary'],
                        "services_affected": list(deletion_results['services_processed'].keys())
                    }
                )
                logger.info("Published user deletion event", user_id=user_id)
            except Exception as e:
                logger.error("Failed to publish user deletion event", error=str(e))
    
    async def _publish_user_deletion_failed_event(self, user_id: str, error: str):
        """Publish user deletion failure event"""
        if self.event_publisher:
            try:
                await self.event_publisher.publish_business_event(
                    event_type="auth.user.deletion_failed",
                    tenant_id="system",
                    data={
                        "user_id": user_id,
                        "error": error,
                        "timestamp": datetime.utcnow().isoformat()
                    }
                )
                logger.info("Published user deletion failure event", user_id=user_id)
            except Exception as e:
                logger.error("Failed to publish deletion failure event", error=str(e))
    
    async def _notify_admins_of_deletion(self, user_info: Dict[str, Any], deletion_results: Dict[str, Any]):
        """Send notification to other admins about the user deletion"""
        try:
            # Get requesting user info for notification
            requesting_user_id = deletion_results['requested_by']
            requesting_user = await self._validate_admin_user(requesting_user_id)
            
            if requesting_user:
                await self.clients.notification_client.send_user_deletion_notification(
                    admin_email=requesting_user['email'],
                    deleted_user_email=user_info['email'],
                    deletion_summary=deletion_results['summary']
                )
                logger.info("Sent user deletion notification", 
                          deleted_user=user_info['email'],
                          notified_admin=requesting_user['email'])
        except Exception as e:
            logger.error("Failed to send admin notification", error=str(e))

    async def preview_user_deletion(self, user_id: str) -> Dict[str, Any]:
        """
        Preview what data would be deleted for an admin user without actually deleting
        """
        try:
            # Get user info
            user_info = await self._validate_admin_user(user_id)
            if not user_info:
                raise HTTPException(
                    status_code=status.HTTP_404_NOT_FOUND,
                    detail=f"Admin user {user_id} not found"
                )
            
            # Get tenant associations
            tenant_info = await self._get_user_tenant_info(user_id)
            
            # Get counts from each service
            training_models_count = 0
            forecasts_count = 0
            notifications_count = 0
            
            for tenant_id in tenant_info['tenant_ids']:
                try:
                    # Get training models count
                    models_count = await self.clients.training_client.get_tenant_models_count(tenant_id)
                    training_models_count += models_count
                    
                    # Get forecasts count
                    tenant_forecasts = await self.clients.forecasting_client.get_tenant_forecasts_count(tenant_id)
                    forecasts_count += tenant_forecasts
                    
                except Exception as e:
                    logger.warning("Could not get counts for tenant", tenant_id=tenant_id, error=str(e))
            
            try:
                # Get user notifications count
                notifications_count = await self.clients.notification_client.get_user_notification_count(user_id)
            except Exception as e:
                logger.warning("Could not get notification count", user_id=user_id, error=str(e))
            
            # Build preview
            preview = {
                "user": user_info,
                "tenant_associations": tenant_info,
                "estimated_deletions": {
                    "training_models": training_models_count,
                    "forecasts": forecasts_count,
                    "notifications": notifications_count,
                    "tenant_memberships": tenant_info['total_tenants'],
                    "owned_tenants": tenant_info['owned_tenants']
                },
                "tenant_handling": await self._preview_tenant_handling(user_id, tenant_info),
                "warning": "This operation is irreversible and will permanently delete all associated data"
            }
            
            return preview
            
        except HTTPException:
            raise
        except Exception as e:
            logger.error("Error generating deletion preview", user_id=user_id, error=str(e))
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Failed to generate deletion preview"
            )
    
    async def _preview_tenant_handling(self, user_id: str, tenant_info: Dict[str, Any]) -> List[Dict[str, Any]]:
        """Preview how each owned tenant would be handled"""
        tenant_handling = []
        
        for membership in tenant_info['memberships']:
            if membership.get('role') == 'owner':
                tenant_id = membership['tenant_id']
                
                try:
                    has_other_admins = await self.clients.tenant_client.check_tenant_has_other_admins(
                        tenant_id, user_id
                    )
                    
                    if has_other_admins:
                        members = await self.clients.tenant_client.get_tenant_members(tenant_id)
                        admin_members = [
                            m for m in members 
                            if m.get('role') == 'admin' and m.get('user_id') != user_id
                        ]
                        
                        tenant_handling.append({
                            "tenant_id": tenant_id,
                            "action": "transfer_ownership",
                            "details": f"Ownership will be transferred to admin: {admin_members[0]['user_id'] if admin_members else 'Unknown'}"
                        })
                    else:
                        tenant_handling.append({
                            "tenant_id": tenant_id,
                            "action": "delete_tenant",
                            "details": "Tenant will be deleted completely (no other admins found)"
                        })
                        
                except Exception as e:
                    tenant_handling.append({
                        "tenant_id": tenant_id,
                        "action": "error",
                        "details": f"Could not determine action: {str(e)}"
                    })
        
        return tenant_handling
