# -*- coding: utf-8 -*-
"""
Accounts Services Module

This module contains service classes for the accounts app, providing
business logic and operations for user management, authentication,
and account-related functionality.

Services:
    - UserService: Core user management operations
    - AuthenticationService: Authentication and authorization
    - ProfileService: User profile management
    - PermissionService: Role and permission management
    - ActivityService: User activity tracking
    - ExportService: Data export functionality

Features:
    - User lifecycle management
    - Authentication and session handling
    - Profile and preference management
    - Role-based access control
    - Activity logging and tracking
    - Data export and reporting
    - Email verification and notifications
    - Password management and security

Usage:
    from apps.accounts.services import UserService, AuthenticationService
    
    # User management
    user_service = UserService()
    user = user_service.create_user(email, password, **data)
    
    # Authentication
    auth_service = AuthenticationService()
    result = auth_service.authenticate_user(email, password)

Security:
    - Password hashing and validation
    - Session management
    - Permission checking
    - Activity logging
    - Rate limiting
"""

import logging
from typing import Dict, List, Optional, Tuple, Any
from datetime import datetime, timedelta
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.models import Permission
from django.core.exceptions import ValidationError
from django.core.mail import send_mail
from django.db import transaction
from django.db.models import Q, Count
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from django.conf import settings
from django.template.loader import render_to_string
from django.urls import reverse
from django.contrib.sites.models import Site

from .models import User
from apps.core.utils import generate_unique_id, send_email_notification

logger = logging.getLogger(__name__)


class UserService:
    """
    Service for user management operations.
    
    Handles user creation, updates, deletion, and lifecycle management
    with proper validation and business logic.
    """
    
    def create_user(self, email: str, password: str, **kwargs) -> User:
        """
        Create a new user with validation.
        
        Args:
            email: User email address
            password: User password
            **kwargs: Additional user data
        
        Returns:
            User: Created user instance
        
        Raises:
            ValidationError: If validation fails
        """
        try:
            with transaction.atomic():
                # Validate email uniqueness
                if User.objects.filter(email=email).exists():
                    raise ValidationError(_('User with this email already exists.'))
                
                # Create user
                user = User.objects.create_user(
                    username=email,
                    email=email,
                    password=password,
                    **kwargs
                )
                
                # Send welcome email if configured
                if getattr(settings, 'SEND_WELCOME_EMAIL', True):
                    self._send_welcome_email(user)
                
                logger.info(f'User created successfully: {user.email}')
                return user
                
        except Exception as e:
            logger.error(f'Error creating user {email}: {e}')
            raise
    
    def update_user(self, user: User, **kwargs) -> User:
        """
        Update user information.
        
        Args:
            user: User instance to update
            **kwargs: Fields to update
        
        Returns:
            User: Updated user instance
        """
        try:
            for field, value in kwargs.items():
                if hasattr(user, field):
                    setattr(user, field, value)
            
            user.save()
            logger.info(f'User updated successfully: {user.email}')
            return user
            
        except Exception as e:
            logger.error(f'Error updating user {user.email}: {e}')
            raise
    
    def deactivate_user(self, user: User, reason: str = None) -> bool:
        """
        Deactivate a user account.
        
        Args:
            user: User to deactivate
            reason: Reason for deactivation
        
        Returns:
            bool: Success status
        """
        try:
            user.is_active = False
            user.deactivated_at = timezone.now()
            user.deactivation_reason = reason
            user.save()
            
            # Log activity
            self._log_user_activity(user, 'deactivated', {'reason': reason})
            
            logger.info(f'User deactivated: {user.email}')
            return True
            
        except Exception as e:
            logger.error(f'Error deactivating user {user.email}: {e}')
            return False
    
    def activate_user(self, user: User) -> bool:
        """
        Activate a user account.
        
        Args:
            user: User to activate
        
        Returns:
            bool: Success status
        """
        try:
            user.is_active = True
            user.deactivated_at = None
            user.deactivation_reason = None
            user.save()
            
            # Log activity
            self._log_user_activity(user, 'activated')
            
            logger.info(f'User activated: {user.email}')
            return True
            
        except Exception as e:
            logger.error(f'Error activating user {user.email}: {e}')
            return False
    
    def get_user_statistics(self) -> Dict[str, Any]:
        """
        Get user statistics.
        
        Returns:
            Dict: User statistics
        """
        try:
            total_users = User.objects.count()
            active_users = User.objects.filter(is_active=True).count()
            inactive_users = total_users - active_users
            
            # Users registered in last 30 days
            thirty_days_ago = timezone.now() - timedelta(days=30)
            new_users = User.objects.filter(
                date_joined__gte=thirty_days_ago
            ).count()
            
            # Users by role
            users_by_role = User.objects.values('role').annotate(
                count=Count('id')
            )
            
            return {
                'total_users': total_users,
                'active_users': active_users,
                'inactive_users': inactive_users,
                'new_users_30_days': new_users,
                'users_by_role': list(users_by_role)
            }
            
        except Exception as e:
            logger.error(f'Error getting user statistics: {e}')
            return {}
    
    def _send_welcome_email(self, user: User) -> bool:
        """
        Send welcome email to new user.
        
        Args:
            user: User to send email to
        
        Returns:
            bool: Success status
        """
        try:
            context = {
                'user': user,
                'site': Site.objects.get_current(),
                'login_url': reverse('authentification:login')
            }
            
            send_email_notification(
                template_name='accounts/emails/welcome',
                context=context,
                to_email=user.email,
                subject=_('Welcome to Ad-tlas')
            )
            
            return True
            
        except Exception as e:
            logger.error(f'Error sending welcome email to {user.email}: {e}')
            return False
    
    def _log_user_activity(self, user: User, action: str, data: Dict = None):
        """
        Log user activity.
        
        Args:
            user: User performing action
            action: Action performed
            data: Additional data
        """
        try:
            # Import here to avoid circular imports
            from apps.activities.models import Activity
            
            Activity.objects.create(
                user=user,
                action=f'user_{action}',
                object_type='user',
                object_id=user.id,
                data=data or {}
            )
            
        except Exception as e:
            logger.error(f'Error logging user activity: {e}')


class AuthenticationService:
    """
    Service for authentication operations.
    
    Handles user authentication, session management,
    and security-related functionality.
    """
    
    def authenticate_user(self, email: str, password: str) -> Tuple[bool, Optional[User], str]:
        """
        Authenticate user credentials.
        
        Args:
            email: User email
            password: User password
        
        Returns:
            Tuple: (success, user, message)
        """
        try:
            user = authenticate(username=email, password=password)
            
            if user is None:
                return False, None, _('Invalid email or password.')
            
            if not user.is_active:
                return False, None, _('Account is deactivated.')
            
            # Update last login
            user.last_login = timezone.now()
            user.save(update_fields=['last_login'])
            
            # Log successful login
            self._log_authentication_event(user, 'login_success')
            
            return True, user, _('Authentication successful.')
            
        except Exception as e:
            logger.error(f'Authentication error for {email}: {e}')
            return False, None, _('Authentication failed.')
    
    def login_user(self, request, user: User) -> bool:
        """
        Log in user and create session.
        
        Args:
            request: HTTP request
            user: User to log in
        
        Returns:
            bool: Success status
        """
        try:
            login(request, user)
            
            # Update last activity
            user.last_activity = timezone.now()
            user.save(update_fields=['last_activity'])
            
            # Log login activity
            self._log_authentication_event(user, 'login', {
                'ip_address': self._get_client_ip(request),
                'user_agent': request.META.get('HTTP_USER_AGENT', '')
            })
            
            return True
            
        except Exception as e:
            logger.error(f'Login error for user {user.email}: {e}')
            return False
    
    def logout_user(self, request) -> bool:
        """
        Log out user and clear session.
        
        Args:
            request: HTTP request
        
        Returns:
            bool: Success status
        """
        try:
            user = request.user
            
            # Log logout activity
            if user.is_authenticated:
                self._log_authentication_event(user, 'logout')
            
            logout(request)
            return True
            
        except Exception as e:
            logger.error(f'Logout error: {e}')
            return False
    
    def _log_authentication_event(self, user: User, event: str, data: Dict = None):
        """
        Log authentication event.
        
        Args:
            user: User involved in event
            event: Event type
            data: Additional data
        """
        try:
            from apps.activities.models import Activity
            
            Activity.objects.create(
                user=user,
                action=f'auth_{event}',
                object_type='authentication',
                object_id=user.id,
                data=data or {}
            )
            
        except Exception as e:
            logger.error(f'Error logging authentication event: {e}')
    
    def _get_client_ip(self, request) -> str:
        """
        Get client IP address from request.
        
        Args:
            request: HTTP request
        
        Returns:
            str: Client IP address
        """
        x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR')
        if x_forwarded_for:
            ip = x_forwarded_for.split(',')[0]
        else:
            ip = request.META.get('REMOTE_ADDR')
        return ip


class ProfileService:
    """
    Service for user profile management.
    
    Handles profile updates, avatar management,
    and user preferences.
    """
    
    def update_profile(self, user: User, data: Dict) -> Tuple[bool, str]:
        """
        Update user profile.
        
        Args:
            user: User to update
            data: Profile data
        
        Returns:
            Tuple: (success, message)
        """
        try:
            # Update allowed fields
            allowed_fields = [
                'first_name', 'last_name', 'phone', 'bio',
                'timezone', 'language'
            ]
            
            for field in allowed_fields:
                if field in data:
                    setattr(user, field, data[field])
            
            user.save()
            
            # Log profile update
            self._log_profile_activity(user, 'profile_updated', data)
            
            return True, _('Profile updated successfully.')
            
        except Exception as e:
            logger.error(f'Error updating profile for {user.email}: {e}')
            return False, _('Failed to update profile.')
    
    def update_avatar(self, user: User, avatar_file) -> Tuple[bool, str]:
        """
        Update user avatar.
        
        Args:
            user: User to update
            avatar_file: Avatar file
        
        Returns:
            Tuple: (success, message)
        """
        try:
            # Validate file
            if avatar_file.size > 5 * 1024 * 1024:  # 5MB
                return False, _('Avatar file size cannot exceed 5MB.')
            
            # Update avatar
            user.avatar = avatar_file
            user.save()
            
            # Log avatar update
            self._log_profile_activity(user, 'avatar_updated')
            
            return True, _('Avatar updated successfully.')
            
        except Exception as e:
            logger.error(f'Error updating avatar for {user.email}: {e}')
            return False, _('Failed to update avatar.')
    
    def _log_profile_activity(self, user: User, action: str, data: Dict = None):
        """
        Log profile activity.
        
        Args:
            user: User performing action
            action: Action performed
            data: Additional data
        """
        try:
            from apps.activities.models import Activity
            
            Activity.objects.create(
                user=user,
                action=f'profile_{action}',
                object_type='profile',
                object_id=user.id,
                data=data or {}
            )
            
        except Exception as e:
            logger.error(f'Error logging profile activity: {e}')


class ExportService:
    """
    Service for data export functionality.
    
    Handles user data export in various formats.
    """
    
    def export_users(self, format: str = 'csv', filters: Dict = None) -> Tuple[bool, str, Any]:
        """
        Export user data.
        
        Args:
            format: Export format (csv, excel, json)
            filters: Query filters
        
        Returns:
            Tuple: (success, message, data)
        """
        try:
            # Get users based on filters
            queryset = User.objects.all()
            
            if filters:
                if 'is_active' in filters:
                    queryset = queryset.filter(is_active=filters['is_active'])
                if 'role' in filters:
                    queryset = queryset.filter(role=filters['role'])
                if 'date_from' in filters:
                    queryset = queryset.filter(date_joined__gte=filters['date_from'])
                if 'date_to' in filters:
                    queryset = queryset.filter(date_joined__lte=filters['date_to'])
            
            # Export based on format
            if format == 'csv':
                return self._export_csv(queryset)
            elif format == 'excel':
                return self._export_excel(queryset)
            elif format == 'json':
                return self._export_json(queryset)
            else:
                return False, _('Unsupported export format.'), None
                
        except Exception as e:
            logger.error(f'Error exporting users: {e}')
            return False, _('Export failed.'), None
    
    def _export_csv(self, queryset) -> Tuple[bool, str, str]:
        """
        Export users to CSV format.
        
        Args:
            queryset: User queryset
        
        Returns:
            Tuple: (success, message, csv_data)
        """
        import csv
        import io
        
        output = io.StringIO()
        writer = csv.writer(output)
        
        # Write header
        writer.writerow([
            'ID', 'Email', 'First Name', 'Last Name',
            'Role', 'Is Active', 'Date Joined', 'Last Login'
        ])
        
        # Write data
        for user in queryset:
            writer.writerow([
                user.id, user.email, user.first_name, user.last_name,
                user.get_role_display(), user.is_active,
                user.date_joined.strftime('%Y-%m-%d %H:%M:%S'),
                user.last_login.strftime('%Y-%m-%d %H:%M:%S') if user.last_login else ''
            ])
        
        return True, _('CSV export completed.'), output.getvalue()
    
    def _export_excel(self, queryset) -> Tuple[bool, str, bytes]:
        """
        Export users to Excel format.
        
        Args:
            queryset: User queryset
        
        Returns:
            Tuple: (success, message, excel_data)
        """
        try:
            import openpyxl
            from openpyxl.utils import get_column_letter
            import io
            
            wb = openpyxl.Workbook()
            ws = wb.active
            ws.title = 'Users'
            
            # Write header
            headers = [
                'ID', 'Email', 'First Name', 'Last Name',
                'Role', 'Is Active', 'Date Joined', 'Last Login'
            ]
            
            for col, header in enumerate(headers, 1):
                ws.cell(row=1, column=col, value=header)
            
            # Write data
            for row, user in enumerate(queryset, 2):
                ws.cell(row=row, column=1, value=user.id)
                ws.cell(row=row, column=2, value=user.email)
                ws.cell(row=row, column=3, value=user.first_name)
                ws.cell(row=row, column=4, value=user.last_name)
                ws.cell(row=row, column=5, value=user.get_role_display())
                ws.cell(row=row, column=6, value=user.is_active)
                ws.cell(row=row, column=7, value=user.date_joined)
                ws.cell(row=row, column=8, value=user.last_login)
            
            # Save to bytes
            output = io.BytesIO()
            wb.save(output)
            output.seek(0)
            
            return True, _('Excel export completed.'), output.getvalue()
            
        except ImportError:
            return False, _('Excel export requires openpyxl package.'), None
    
    def _export_json(self, queryset) -> Tuple[bool, str, str]:
        """
        Export users to JSON format.
        
        Args:
            queryset: User queryset
        
        Returns:
            Tuple: (success, message, json_data)
        """
        import json
        from django.core.serializers.json import DjangoJSONEncoder
        
        data = []
        for user in queryset:
            data.append({
                'id': user.id,
                'email': user.email,
                'first_name': user.first_name,
                'last_name': user.last_name,
                'role': user.role,
                'is_active': user.is_active,
                'date_joined': user.date_joined,
                'last_login': user.last_login
            })
        
        json_data = json.dumps(data, cls=DjangoJSONEncoder, indent=2)
        return True, _('JSON export completed.'), json_data