"""
Enhanced Portfolio Analytics Views
Provides comprehensive portfolio performance analysis and dashboard components
"""

from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required
from django.contrib import messages
from django.http import JsonResponse, HttpResponse
from django.views.decorators.http import require_http_methods
from django.utils import timezone
from django.db.models import Q
from django.core.paginator import Paginator
from django.db import transaction
from datetime import datetime, timedelta
import json
import logging
from decimal import Decimal
from django.core.serializers.json import DjangoJSONEncoder

from .models import CustomUser, Branch, PortfolioAssignment
from .services import PortfolioAnalyticsService
from .enhanced_permissions_models import PortfolioSnapshot
from loans.models import Loan
from utils.models import AuditLog
from django.db.models import Sum, Count, Q
from django.db import models

logger = logging.getLogger(__name__)


class DecimalEncoder(DjangoJSONEncoder):
    """JSON encoder that handles Decimal objects"""
    def default(self, obj):
        if isinstance(obj, Decimal):
            return float(obj)
        return super().default(obj)


@login_required
def portfolio_dashboard(request):
    """
    Basic portfolio dashboard with simple overview
    """
    try:
        user = request.user
        if user.role not in ['admin', 'team_leader', 'loan_officer']:
            return render(request, 'errors/403.html', {
                'message': 'You do not have permission to access portfolio analytics.'
            })
        
        # Get portfolio managers based on user role
        if user.role == 'admin':
            portfolio_managers = CustomUser.objects.filter(
                role__in=['loan_officer', 'team_leader'],
                status='active'
            ).select_related('branch')
        elif user.role == 'team_leader':
            portfolio_managers = CustomUser.objects.filter(
                role__in=['loan_officer', 'team_leader'],
                branch=user.branch,
                status='active'
            ).select_related('branch')
        else:
            portfolio_managers = CustomUser.objects.filter(id=user.id)
        
        # Calculate total clients (assigned + unassigned)
        if user.role == 'admin':
            total_clients = CustomUser.objects.filter(
                role='borrower',
                status='active'
            ).count()
        elif user.role == 'team_leader':
            total_clients = CustomUser.objects.filter(
                role='borrower',
                status='active',
                branch=user.branch
            ).count()
        else:
            total_clients = CustomUser.objects.filter(
                role='borrower',
                status='active',
                portfolio_manager=user
            ).count()
        
        # Calculate assigned clients
        if user.role == 'admin':
            total_assigned = CustomUser.objects.filter(
                role='borrower',
                status='active',
                portfolio_manager__isnull=False
            ).count()
        elif user.role == 'team_leader':
            total_assigned = CustomUser.objects.filter(
                role='borrower',
                status='active',
                portfolio_manager__isnull=False,
                branch=user.branch
            ).count()
        else:
            total_assigned = CustomUser.objects.filter(
                role='borrower',
                status='active',
                portfolio_manager=user
            ).count()
        
        # Calculate unassigned clients
        if user.role == 'admin':
            unassigned_clients_query = CustomUser.objects.filter(
                role='borrower',
                status='active',
                portfolio_manager__isnull=True
            )
            unassigned_clients = unassigned_clients_query.count()
            unassigned_clients_list = unassigned_clients_query
        elif user.role == 'team_leader':
            unassigned_clients_query = CustomUser.objects.filter(
                role='borrower',
                status='active',
                portfolio_manager__isnull=True,
                branch=user.branch
            )
            unassigned_clients = unassigned_clients_query.count()
            unassigned_clients_list = unassigned_clients_query
        else:
            unassigned_clients = 0
            unassigned_clients_list = CustomUser.objects.none()
        
        # Calculate assignment rate
        assignment_rate = (total_assigned / total_clients * 100) if total_clients > 0 else 0
        
        # Get manager stats
        manager_stats = []
        for manager in portfolio_managers:
            stats = manager.get_portfolio_stats()
            if stats:
                default_rate = stats.get('default_rate')
                manager_stats.append({
                    'manager': manager,
                    'total_clients': stats['total_clients'],
                    'active_loans': stats['active_loans'],
                    'total_disbursed': stats['total_disbursed'],
                    'collection_rate': float(stats.get('collection_rate', 0)),
                    'default_rate': float(default_rate) if default_rate is not None else None,
                })
        
        # Sort managers by total clients (descending)
        manager_stats.sort(key=lambda x: x['total_clients'], reverse=True)
        
        context = {
            'total_clients': total_clients,
            'total_assigned': total_assigned,
            'unassigned_clients': unassigned_clients,
            'assignment_rate': assignment_rate,
            'manager_stats': manager_stats,
            'unassigned_clients_list': unassigned_clients_list,
        }
        
        return render(request, 'users/portfolio_dashboard.html', context)
        
    except Exception as e:
        logger.error(f"Error in portfolio dashboard: {e}")
        return render(request, 'errors/500.html', {
            'message': 'An error occurred while loading portfolio dashboard.'
        })


@login_required
def enhanced_portfolio_dashboard(request):
    """
    Enhanced portfolio dashboard with comprehensive analytics
    """
    # Check if user has permission to access portfolio
    if not request.user.has_permission('portfolio', 'access'):
        messages.error(request, 'You do not have permission to access portfolio.')
        return redirect('dashboard')
    try:
        # Initialize analytics service
        analytics_service = PortfolioAnalyticsService()
        
        # Get current user and check permissions
        user = request.user
        if user.role not in ['admin', 'team_leader', 'loan_officer']:
            return render(request, 'errors/403.html', {
                'message': 'You do not have permission to access portfolio analytics.'
            })
        
        # Get date range from request
        date_range_days = int(request.GET.get('days', 30))
        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=date_range_days)
        date_range = (start_date.isoformat(), end_date.isoformat())
        
        # Get portfolio managers based on user role
        if user.role == 'admin':
            # Admin can see all portfolio managers
            portfolio_managers = CustomUser.objects.filter(
                role__in=['loan_officer', 'team_leader'],
                status='active'
            ).select_related('branch')
        elif user.role == 'team_leader':
            # Team leaders can see their branch managers
            portfolio_managers = CustomUser.objects.filter(
                role__in=['loan_officer', 'team_leader'],
                branch=user.branch,
                status='active'
            ).select_related('branch')
        else:
            # Loan officers can only see their own portfolio
            portfolio_managers = CustomUser.objects.filter(id=user.id)
        
        # Get analytics for each manager
        manager_analytics = []
        total_portfolio_value = 0
        total_clients = 0
        total_collection_rate = 0
        
        for manager in portfolio_managers:
            try:
                # Use basic stats if analytics service fails
                try:
                    overview = analytics_service.get_portfolio_overview(
                        str(manager.id), 
                        date_range
                    )
                except Exception as e:
                    logger.warning(f"Analytics service failed for {manager.id}, using basic stats: {e}")
                    overview = None
                
                if overview and 'error' not in overview:
                    manager_data = {
                        'manager': manager,
                        'overview': overview,
                        'performance_score': calculate_performance_score(overview),
                        'risk_level': overview.get('risk_metrics', {}).get('risk_level', 'medium'),
                        'total_clients': overview['client_metrics']['total_clients'],
                        'active_loans': overview['loan_metrics']['active_loans'],
                        'collection_rate': overview['performance_metrics']['collection_rate'],
                        'default_rate': overview['performance_metrics']['default_rate'],
                        'total_disbursed': overview['loan_metrics']['total_disbursed'],
                        'total_outstanding': overview['loan_metrics']['total_outstanding']
                    }
                    
                    manager_analytics.append(manager_data)
                    
                    # Aggregate totals
                    total_portfolio_value += overview['loan_metrics']['total_outstanding']
                    total_clients += overview['client_metrics']['total_clients']
                    total_collection_rate += overview['performance_metrics']['collection_rate']
                else:
                    # Fallback to basic stats from manager model
                    stats = manager.get_portfolio_stats()
                    clients_count = manager.portfolio_clients.filter(status='active').count()
                    active_loans = Loan.objects.filter(borrower__portfolio_manager=manager, status='active').count()
                    
                    manager_data = {
                        'manager': manager,
                        'overview': None,
                        'performance_score': 0,
                        'risk_level': 'low',
                        'total_clients': clients_count,
                        'active_loans': active_loans,
                        'collection_rate': float(stats.get('collection_rate', 0)) if stats else 0,
                        'default_rate': float(stats.get('default_rate', 0)) if stats else 0,
                        'total_disbursed': float(stats.get('total_disbursed', 0)) if stats else 0,
                        'total_outstanding': float(stats.get('total_outstanding', 0)) if stats else 0
                    }
                    
                    manager_analytics.append(manager_data)
                    
                    total_portfolio_value += manager_data['total_outstanding']
                    total_clients += clients_count
                    total_collection_rate += manager_data['collection_rate']
                
            except Exception as e:
                logger.error(f"Error getting analytics for manager {manager.id}: {e}")
                continue
        
        # Calculate averages
        avg_collection_rate = (total_collection_rate / len(manager_analytics)) if manager_analytics else 0
        
        # Sort managers by performance score
        manager_analytics.sort(key=lambda x: x['performance_score'], reverse=True)
        
        # Prepare chart data
        chart_data = prepare_chart_data(manager_analytics)
        
        # Get risk alerts
        risk_alerts = get_portfolio_risk_alerts(manager_analytics)
        
        # Get performance trends
        performance_trends = get_performance_trends(portfolio_managers, analytics_service)
        
        # Get portfolio comparison data
        portfolio_comparison = get_portfolio_comparison_data(manager_analytics)
        
        # Get benchmarking data
        benchmarking_data = get_benchmarking_data(manager_analytics, analytics_service)
        
        context = {
            'manager_rankings': manager_analytics,
            'total_portfolio_value': total_portfolio_value,
            'total_managers': len(manager_analytics),
            'avg_collection_rate': avg_collection_rate,
            'total_clients': total_clients,
            'chart_data': json.dumps(chart_data),
            'risk_alerts': risk_alerts,
            'performance_trends': performance_trends,
            'portfolio_comparison': portfolio_comparison,
            'benchmarking_data': benchmarking_data,
        }
        
        return render(request, 'users/enhanced_portfolio_analytics.html', context)
        
    except Exception as e:
        logger.error(f"Error in enhanced portfolio dashboard: {e}")
        return render(request, 'errors/500.html', {
            'message': 'An error occurred while loading enhanced portfolio analytics.'
        })


@login_required
def portfolio_manager_detail(request, manager_id):
    """Detailed view of a portfolio manager's portfolio"""
    # Check if user has permission to access portfolio
    if not request.user.has_permission('portfolio', 'access'):
        messages.error(request, 'You do not have permission to access portfolio.')
        return redirect('dashboard')
    manager = get_object_or_404(CustomUser, id=manager_id, role__in=['loan_officer', 'team_leader'])
    
    if not request.user.role in ['admin', 'team_leader'] and request.user != manager:
        messages.error(request, 'You do not have permission to view this portfolio.')
        return redirect('users:portfolio_dashboard')
    
    # Get portfolio statistics
    stats = manager.get_portfolio_stats()
    
    # Convert Decimal values to float for template rendering
    if stats:
        if 'default_rate' in stats and stats['default_rate'] is not None:
            stats['default_rate'] = float(stats['default_rate'])
        if 'collection_rate' in stats and stats['collection_rate'] is not None:
            stats['collection_rate'] = float(stats['collection_rate'])
    
    # Get clients in portfolio with active loans count
    clients = manager.portfolio_clients.filter(status='active').select_related('portfolio_manager')
    
    # Add active loans count to each client
    for client in clients:
        client.active_loans_count = client.loans.filter(status='active').count()
    
    # Pagination for clients
    paginator = Paginator(clients, 20)
    page_number = request.GET.get('page')
    clients_page = paginator.get_page(page_number)
    
    # Get recent loans
    client_ids = list(clients.values_list('id', flat=True))
    recent_loans = Loan.objects.filter(
        borrower_id__in=client_ids
    ).select_related('borrower').order_by('-created_at')[:10]
    
    # Get performance trend
    performance_trend = manager.get_portfolio_performance_trend(6)
    
    context = {
        'manager': manager,
        'stats': stats,
        'clients': clients_page,
        'clients_count': clients.count(),  # Total count for display
        'recent_loans': recent_loans,
        'performance_trend': json.dumps(performance_trend, cls=DecimalEncoder),
    }
    
    return render(request, 'users/portfolio_manager_detail.html', context)


@login_required
def assign_clients(request):
    """Modern Portfolio Assignment Interface"""
    # Check if user has permission to assign clients
    if not request.user.has_permission('clients', 'assign'):
        messages.error(request, 'You do not have permission to assign clients.')
        return redirect('users:portfolio_dashboard')
    
    # Check if user role is None or empty
    if not request.user.role:
        messages.error(request, 'Your user account does not have a role assigned. Please contact an administrator.')
        return redirect('users:portfolio_dashboard')
    
    # Check permissions - allow admin, team_leader, loan_officer, secretary, and auditor
    if not request.user.role in ['admin', 'team_leader', 'loan_officer', 'secretary', 'auditor']:
        messages.error(request, f'You do not have permission to assign clients. Your role: {request.user.role}')
        return redirect('users:portfolio_dashboard')
    
    # Get selected branch from session or request parameter for filtering
    selected_branch_id = request.session.get('selected_branch_id')
    if 'branch' in request.GET:
        selected_branch_id = request.GET.get('branch')
        request.session['selected_branch_id'] = selected_branch_id
    
    if request.method == 'POST':
        try:
            # Check if this is an unassign action
            if request.POST.get('action') == 'unassign':
                return handle_unassign_clients(request)
            
            assignment_type = request.POST.get('assignment_type', 'manual')
            if assignment_type == 'bulk_auto':
                return handle_bulk_auto_assignment(request, selected_branch_id)
            elif assignment_type == 'smart_assign':
                return handle_smart_assignment(request, selected_branch_id)
            else:
                return handle_modern_assignment(request, selected_branch_id)
        except Exception as e:
            messages.error(request, f'Error during assignment: {str(e)}')
            return redirect('users:assign_clients')
    
    # GET request - show modern assignment interface
    try:
        # Get all branches for filtering options
        from users.models import Branch
        all_branches = Branch.objects.all().order_by('name')
        current_branch = None
        if selected_branch_id:
            try:
                current_branch = Branch.objects.get(id=selected_branch_id)
            except Branch.DoesNotExist:
                selected_branch_id = None
        
        # Get unassigned clients with enhanced data and flexible branch filtering
        unassigned_clients_query = CustomUser.objects.filter(
            role='borrower',
            status='active',
            portfolio_manager__isnull=True
        ).select_related('branch').prefetch_related('loans')
        
        # Apply flexible branch filtering - show clients from selected branch OR clients without branches
        if selected_branch_id:
            unassigned_clients_query = unassigned_clients_query.filter(
                models.Q(branch_id=selected_branch_id) | models.Q(branch__isnull=True)
            )
        
        # Get all unassigned clients (remove limit for better visibility)
        unassigned_clients = unassigned_clients_query.order_by('first_name', 'last_name')
        
        # Get available portfolio managers with enhanced data
        portfolio_managers_query = CustomUser.objects.filter(
            role__in=['loan_officer', 'team_leader', 'secretary', 'auditor'],
            status='active'
        ).select_related('branch')
        
        # Apply branch filtering for managers - show managers from selected branch OR all managers if no branch selected
        if selected_branch_id:
            portfolio_managers_query = portfolio_managers_query.filter(branch_id=selected_branch_id)
        
        portfolio_managers = portfolio_managers_query.order_by('first_name', 'last_name')
        
        # Add enhanced client count and statistics to each manager
        for manager in portfolio_managers:
            # Total clients assigned to this manager
            manager.client_count = CustomUser.objects.filter(
                portfolio_manager=manager,
                status='active'
            ).count()
            
            # Branch-specific client count if branch is selected
            if selected_branch_id:
                manager.branch_client_count = CustomUser.objects.filter(
                    portfolio_manager=manager,
                    status='active',
                    branch_id=selected_branch_id
                ).count()
            else:
                manager.branch_client_count = manager.client_count
            
            # Get manager's portfolio statistics
            manager.active_loans = Loan.objects.filter(
                borrower__portfolio_manager=manager,
                status__in=['active', 'disbursed']
            ).count()
            
            manager.total_portfolio_value = Loan.objects.filter(
                borrower__portfolio_manager=manager,
                status__in=['active', 'disbursed']
            ).aggregate(total=Sum('principal_amount'))['total'] or 0
        
        # Get recent assignments with enhanced data
        recent_assignments_query = PortfolioAssignment.objects.filter(
            is_active=True,
            assigned_date__gte=timezone.now() - timedelta(days=30)  # Show last 30 days
        ).select_related('client', 'portfolio_manager', 'assigned_by')
        
        if selected_branch_id:
            recent_assignments_query = recent_assignments_query.filter(
                models.Q(client__branch_id=selected_branch_id) | models.Q(client__branch__isnull=True)
            )
        
        recent_assignments = recent_assignments_query.order_by('-assigned_date')[:20]
        
        # Calculate comprehensive statistics
        total_unassigned = unassigned_clients.count()
        total_managers = portfolio_managers.count()
        avg_clients_per_manager = sum(m.branch_client_count for m in portfolio_managers) / total_managers if total_managers > 0 else 0
        
        # Get total clients in branch (assigned + unassigned)
        total_clients_in_branch = CustomUser.objects.filter(
            role='borrower',
            status='active'
        )
        if selected_branch_id:
            total_clients_in_branch = total_clients_in_branch.filter(
                models.Q(branch_id=selected_branch_id) | models.Q(branch__isnull=True)
            )
        total_clients_in_branch = total_clients_in_branch.count()
        
        # Get assigned clients count
        assigned_clients_count = total_clients_in_branch - total_unassigned
        assignment_rate = (assigned_clients_count / total_clients_in_branch * 100) if total_clients_in_branch > 0 else 0
        
        # Get additional analytics data
        from django.db.models import Count, Avg
        
        # Portfolio performance analytics
        portfolio_analytics = []
        for manager in portfolio_managers:
            manager_loans = Loan.objects.filter(
                borrower__portfolio_manager=manager,
                status__in=['active', 'disbursed']
            )
            
            analytics = {
                'manager': manager,
                'total_loans': manager_loans.count(),
                'total_value': manager_loans.aggregate(total=Sum('principal_amount'))['total'] or 0,
                'avg_loan_size': manager_loans.aggregate(avg=Avg('principal_amount'))['avg'] or 0,
                'overdue_loans': manager_loans.filter(status='overdue').count(),
                'client_count': manager.branch_client_count,
            }
            portfolio_analytics.append(analytics)
        
        # Branch statistics
        branch_stats = {
            'current_branch': current_branch,
            'all_branches': all_branches,
            'total_clients_in_branch': total_clients_in_branch,
            'assigned_clients_count': assigned_clients_count,
            'unassigned_clients_count': total_unassigned,
            'assignment_rate': round(assignment_rate, 1),
        }
        
        context = {
            'unassigned_clients': unassigned_clients,
            'portfolio_managers': portfolio_managers,
            'recent_assignments': recent_assignments,
            'selected_branch_id': selected_branch_id,
            'portfolio_analytics': portfolio_analytics,
            'branch_stats': branch_stats,
            'stats': {
                'total_unassigned': total_unassigned,
                'total_managers': total_managers,
                'avg_clients_per_manager': round(avg_clients_per_manager, 1),
                'total_clients_in_branch': total_clients_in_branch,
                'assigned_clients_count': assigned_clients_count,
                'assignment_rate': round(assignment_rate, 1),
            }
        }
        
        return render(request, 'users/assign_clients_simple.html', context)
        
    except Exception as e:
        import traceback
        traceback.print_exc()
        messages.error(request, f'Error loading assignment page: {str(e)}')
        return redirect('users:portfolio_dashboard')


def handle_modern_assignment(request, selected_branch_id):
    """Handle modern client assignment with enhanced features"""
    client_ids = request.POST.getlist('client_ids')
    manager_id = request.POST.get('manager_id')
    assignment_type = request.POST.get('assignment_type', 'manual')
    
    if not client_ids or not manager_id:
        messages.error(request, 'Please select clients and a manager.')
        return redirect('users:assign_clients')
    
    try:
        # Get the manager
        manager = CustomUser.objects.get(id=manager_id, role__in=['loan_officer', 'team_leader', 'secretary', 'auditor'])
        
        # Get the clients
        clients = CustomUser.objects.filter(
            id__in=client_ids,
            role='borrower',
            status='active',
            portfolio_manager__isnull=True
        )
        
        if selected_branch_id:
            # For portfolio assignment, we allow more flexibility:
            # - Clients from the selected branch
            # - Clients without branches
            # - Clients from any branch (for cross-branch assignments)
            # This ensures portfolio assignment works regardless of branch restrictions
            pass  # Remove branch filtering for portfolio assignment
        
        if not clients.exists():
            messages.error(request, 'No valid clients found for assignment.')
            return redirect('users:assign_clients')
        
        # Create assignments
        assignments_created = 0
        with transaction.atomic():
            for client in clients:
                # Create portfolio assignment
                assignment = PortfolioAssignment.objects.create(
                    client=client,
                    portfolio_manager=manager,
                    assigned_by=request.user,
                    assigned_date=timezone.now(),
                    is_active=True,
                    reason=f'Assigned via modern interface by {request.user.get_full_name()}'
                )
                
                # Update client's portfolio manager
                client.portfolio_manager = manager
                client.save()
                
                assignments_created += 1
                
                # Log the assignment
                AuditLog.objects.create(
                    user=request.user,
                    action='ASSIGN_CLIENT',
                    description=f'Assigned client {client.get_full_name()} to manager {manager.get_full_name()}',
                    ip_address=request.META.get('REMOTE_ADDR', ''),
                    user_agent=request.META.get('HTTP_USER_AGENT', '')
                )
        
        messages.success(request, f'Successfully assigned {assignments_created} client(s) to {manager.get_full_name()}.')
        return redirect('users:assign_clients')
        
    except CustomUser.DoesNotExist:
        messages.error(request, 'Invalid manager selected.')
        return redirect('users:assign_clients')
    except Exception as e:
        messages.error(request, f'Error during assignment: {str(e)}')
        return redirect('users:assign_clients')


def handle_unassign_clients(request):
    """Handle unassigning clients from portfolio managers"""
    client_ids = request.POST.getlist('client_ids')
    
    if not client_ids:
        messages.error(request, 'No clients selected for unassignment.')
        return redirect('users:assign_clients')
    
    try:
        # Get the clients
        clients = CustomUser.objects.filter(
            id__in=client_ids,
            role='borrower',
            status='active',
            portfolio_manager__isnull=False
        )
        
        if not clients.exists():
            messages.error(request, 'No valid assigned clients found.')
            return redirect('users:assign_clients')
        
        # Unassign clients
        unassigned_count = 0
        with transaction.atomic():
            for client in clients:
                # Deactivate current assignment
                PortfolioAssignment.objects.filter(
                    client=client,
                    is_active=True
                ).update(is_active=False, unassigned_date=timezone.now())
                
                # Clear portfolio manager
                client.portfolio_manager = None
                client.assigned_date = None
                client.save()
                
                unassigned_count += 1
                
                # Log the unassignment
                AuditLog.objects.create(
                    user=request.user,
                    action='UNASSIGN_CLIENT',
                    description=f'Unassigned client {client.get_full_name()} from portfolio manager',
                    ip_address=request.META.get('REMOTE_ADDR', ''),
                    user_agent=request.META.get('HTTP_USER_AGENT', '')
                )
        
        messages.success(request, f'Successfully unassigned {unassigned_count} client(s).')
        
    except Exception as e:
        messages.error(request, f'Error during unassignment: {str(e)}')
    
    return redirect('users:assign_clients')


def handle_manual_assignment(request, selected_branch_id):
    """Handle manual client assignment (legacy)"""
    client_ids = request.POST.getlist('client_ids')
    manager_id = request.POST.get('manager_id')
    reason = request.POST.get('reason', 'Manual portfolio assignment')
    
    # Enhanced validation
    if not client_ids:
        messages.error(request, 'Please select at least one client to assign.')
        return redirect('users:assign_clients')
    
    if not manager_id:
        messages.error(request, 'Please select a portfolio manager.')
        return redirect('users:assign_clients')
    
    # Get manager and validate
    try:
        manager = CustomUser.objects.get(
            id=manager_id, 
            role__in=['loan_officer', 'team_leader', 'manager'],
            status='active'
        )
    except CustomUser.DoesNotExist:
        messages.error(request, 'Invalid portfolio manager selected.')
        return redirect('users:assign_clients')
    
    # Check manager capacity
    current_clients = manager.portfolio_clients.filter(status='active').count()
    max_capacity = getattr(manager, 'max_clients', 50)
    
    if current_clients + len(client_ids) > max_capacity:
        messages.error(request, f'Assignment would exceed manager capacity limit of {max_capacity}. Current: {current_clients}, Attempting to add: {len(client_ids)}')
        return redirect('users:assign_clients')
    
    # Get clients and validate they are unassigned
    clients_query = CustomUser.objects.filter(
        id__in=client_ids, 
        role='borrower',
        status='active',
        portfolio_manager__isnull=True
    )
    
    if selected_branch_id:
        clients_query = clients_query.filter(branch_id=selected_branch_id)
    
    clients = clients_query
    
    if not clients.exists():
        messages.error(request, 'No valid unassigned clients selected.')
        return redirect('users:assign_clients')
    
    # Process assignments with proper transaction handling
    assigned_count = 0
    assignment_details = []
    
    with transaction.atomic():
        for client in clients:
            # Double-check client is still unassigned
            if client.portfolio_manager is not None:
                continue
            
            # Update client's portfolio manager
            client.portfolio_manager = manager
            client.assigned_date = get_current_datetime()
            client.save()
            
            # Create assignment record
            PortfolioAssignment.objects.create(
                client=client,
                portfolio_manager=manager,
                assigned_by=request.user,
                assigned_date=get_current_datetime(),
                reason=reason,
                is_active=True
            )
            
            assignment_details.append(client.get_full_name())
            assigned_count += 1
    
    if assigned_count > 0:
        messages.success(
            request, 
            f'Successfully assigned {assigned_count} client(s) to {manager.get_full_name()}: {", ".join(assignment_details[:3])}{"..." if len(assignment_details) > 3 else ""}'
        )
    else:
        messages.warning(request, 'No clients were assigned. They may have been assigned by another user.')
    
    return redirect('users:portfolio_dashboard')


@login_required
def portfolio_assignment_api(request):
    """API endpoint for real-time portfolio assignment data"""
    if not request.user.role in ['admin', 'team_leader', 'manager']:
        return JsonResponse({'error': 'Permission denied'}, status=403)
    
    selected_branch_id = request.session.get('selected_branch_id')
    
    # Get real-time statistics
    unassigned_count = CustomUser.objects.filter(
        role='borrower',
        status='active',
        portfolio_manager__isnull=True
    )
    if selected_branch_id:
        unassigned_count = unassigned_count.filter(
            models.Q(branch_id=selected_branch_id) | models.Q(branch__isnull=True)
        )
    unassigned_count = unassigned_count.count()
    
    managers_count = CustomUser.objects.filter(
        role__in=['loan_officer', 'team_leader', 'manager'],
        status='active'
    )
    if selected_branch_id:
        managers_count = managers_count.filter(branch_id=selected_branch_id)
    managers_count = managers_count.count()
    
    # Get recent assignments
    recent_assignments = PortfolioAssignment.objects.filter(
        is_active=True,
        assigned_date__gte=timezone.now() - timedelta(days=7)
    ).select_related('client', 'portfolio_manager').order_by('-assigned_date')[:5]
    
    assignments_data = []
    for assignment in recent_assignments:
        assignments_data.append({
            'client_name': assignment.client.get_full_name(),
            'manager_name': assignment.portfolio_manager.get_full_name(),
            'assigned_date': assignment.assigned_date.strftime('%Y-%m-%d %H:%M'),
            'time_ago': str(timezone.now() - assignment.assigned_date)
        })
    
    return JsonResponse({
        'unassigned_count': unassigned_count,
        'managers_count': managers_count,
        'recent_assignments': assignments_data,
        'timestamp': timezone.now().isoformat()
    })


def handle_smart_assignment(request, selected_branch_id):
    """Handle intelligent client assignment using various algorithms"""
    strategy = request.POST.get('smart_strategy', 'workload_balance')
    max_assignments = int(request.POST.get('max_assignments', 50))
    
    # Get unassigned clients
    unassigned_clients_query = CustomUser.objects.filter(
        role='borrower',
        status='active',
        portfolio_manager__isnull=True
    ).select_related('branch')
    
    if selected_branch_id:
        unassigned_clients_query = unassigned_clients_query.filter(
            models.Q(branch_id=selected_branch_id) | models.Q(branch__isnull=True)
        )
    
    unassigned_clients = list(unassigned_clients_query[:max_assignments])
    
    if not unassigned_clients:
        messages.warning(request, 'No unassigned clients found for smart assignment.')
        return redirect('users:assign_clients')
    
    # Get available managers with enhanced statistics
    managers_query = CustomUser.objects.filter(
        role__in=['loan_officer', 'team_leader', 'manager'],
        status='active'
    ).select_related('branch').annotate(
        current_clients=Count('portfolio_clients', filter=Q(portfolio_clients__status='active')),
        total_loans=Count('portfolio_clients__loans', filter=Q(portfolio_clients__loans__status__in=['active', 'disbursed'])),
        portfolio_value=Sum('portfolio_clients__loans__principal_amount', filter=Q(portfolio_clients__loans__status__in=['active', 'disbursed']))
    )
    
    if selected_branch_id:
        managers_query = managers_query.filter(branch_id=selected_branch_id)
    
    managers = list(managers_query.order_by('current_clients'))
    
    if not managers:
        messages.error(request, 'No available portfolio managers found.')
        return redirect('users:assign_clients')
    
    assignments_created = 0
    
    with transaction.atomic():
        if strategy == 'workload_balance':
            # Distribute clients evenly based on current workload
            for i, client in enumerate(unassigned_clients):
                manager = managers[i % len(managers)]
                
                # Create assignment
                PortfolioAssignment.objects.create(
                    client=client,
                    portfolio_manager=manager,
                    assigned_by=request.user,
                    assigned_date=timezone.now(),
                    is_active=True,
                    reason=f'Smart assignment (workload balance) by {request.user.get_full_name()}'
                )
                
                # Update client
                client.portfolio_manager = manager
                client.save()
                
                assignments_created += 1
                
        elif strategy == 'branch_optimization':
            # Assign clients to managers in the same branch when possible
            for client in unassigned_clients:
                # Find manager in same branch with lowest workload
                same_branch_managers = [m for m in managers if m.branch == client.branch]
                if same_branch_managers:
                    manager = min(same_branch_managers, key=lambda m: m.current_clients)
                else:
                    # Fallback to any manager with lowest workload
                    manager = min(managers, key=lambda m: m.current_clients)
                
                # Create assignment
                PortfolioAssignment.objects.create(
                    client=client,
                    portfolio_manager=manager,
                    assigned_by=request.user,
                    assigned_date=timezone.now(),
                    is_active=True,
                    reason=f'Smart assignment (branch optimization) by {request.user.get_full_name()}'
                )
                
                # Update client
                client.portfolio_manager = manager
                client.save()
                
                assignments_created += 1
                
        elif strategy == 'experience_based':
            # Assign based on manager experience and portfolio size
            # Team leaders get more clients, then managers, then loan officers
            role_priority = {'team_leader': 3, 'manager': 2, 'loan_officer': 1}
            managers.sort(key=lambda m: (role_priority.get(m.role, 0), -m.current_clients))
            
            for i, client in enumerate(unassigned_clients):
                manager = managers[i % len(managers)]
                
                # Create assignment
                PortfolioAssignment.objects.create(
                    client=client,
                    portfolio_manager=manager,
                    assigned_by=request.user,
                    assigned_date=timezone.now(),
                    is_active=True,
                    reason=f'Smart assignment (experience-based) by {request.user.get_full_name()}'
                )
                
                # Update client
                client.portfolio_manager = manager
                client.save()
                
                assignments_created += 1
    
    messages.success(request, f'Smart assignment completed! Successfully assigned {assignments_created} client(s) using {strategy.replace("_", " ")} strategy.')
    return redirect('users:assign_clients')


def handle_bulk_auto_assignment(request, selected_branch_id):
    """Handle automatic bulk assignment using various strategies"""
    strategy = request.POST.get('auto_strategy', 'round_robin')
    max_assignments = int(request.POST.get('max_assignments', 100))
    reason = request.POST.get('reason', 'Automatic bulk assignment')
    
    # Get unassigned clients
    unassigned_clients_query = CustomUser.objects.filter(
        role='borrower',
        status='active',
        portfolio_manager__isnull=True
    )
    
    if selected_branch_id:
        unassigned_clients_query = unassigned_clients_query.filter(branch_id=selected_branch_id)
    
    unassigned_clients = list(unassigned_clients_query[:max_assignments])
    
    if not unassigned_clients:
        messages.warning(request, 'No unassigned clients found.')
        return redirect('users:assign_clients')
    
    # Get available managers
    managers_query = CustomUser.objects.filter(
        role__in=['loan_officer', 'team_leader', 'manager'],
        status='active'
    ).annotate(
        current_clients=Count('portfolio_clients', filter=Q(portfolio_clients__status='active'))
    )
    
    if selected_branch_id:
        managers_query = managers_query.filter(branch_id=selected_branch_id)
    
    managers = list(managers_query.order_by('current_clients'))
    
    if not managers:
        messages.error(request, 'No available portfolio managers found.')
        return redirect('users:assign_clients')
    
    assignments = []
    
    with transaction.atomic():
        if strategy == 'round_robin':
            # Round robin assignment
            for i, client in enumerate(unassigned_clients):
                manager = managers[i % len(managers)]
                
                # Check capacity
                max_capacity = getattr(manager, 'max_clients', 50)
                if manager.current_clients >= max_capacity:
                    continue
                
                client.portfolio_manager = manager
                client.assigned_date = get_current_datetime()
                client.save()
                
                PortfolioAssignment.objects.create(
                    client=client,
                    portfolio_manager=manager,
                    assigned_by=request.user,
                    reason=f'{reason} (Round Robin)',
                    is_active=True
                )
                
                assignments.append((client, manager))
                manager.current_clients += 1
        
        elif strategy == 'least_loaded':
            # Assign to least loaded managers first
            for client in unassigned_clients:
                # Find manager with least clients
                available_manager = min(
                    [m for m in managers if m.current_clients < getattr(m, 'max_clients', 50)],
                    key=lambda m: m.current_clients,
                    default=None
                )
                
                if not available_manager:
                    break
                
                client.portfolio_manager = available_manager
                client.assigned_date = get_current_datetime()
                client.save()
                
                PortfolioAssignment.objects.create(
                    client=client,
                    portfolio_manager=available_manager,
                    assigned_by=request.user,
                    reason=f'{reason} (Least Loaded)',
                    is_active=True
                )
                
                assignments.append((client, available_manager))
                available_manager.current_clients += 1
        
        elif strategy == 'geographic':
            # Assign based on geographic proximity (if location data available)
            for client in unassigned_clients:
                # Simple geographic assignment based on branch
                suitable_managers = [m for m in managers if m.branch_id == client.branch_id] if hasattr(client, 'branch_id') else managers
                
                if suitable_managers:
                    manager = min(suitable_managers, key=lambda m: m.current_clients)
                    
                    if manager.current_clients < getattr(manager, 'max_clients', 50):
                        client.portfolio_manager = manager
                        client.assigned_date = get_current_datetime()
                        client.save()
                        
                        PortfolioAssignment.objects.create(
                            client=client,
                            portfolio_manager=manager,
                            assigned_by=request.user,
                            reason=f'{reason} (Geographic)',
                            is_active=True
                        )
                        
                        assignments.append((client, manager))
                        manager.current_clients += 1
    
    if assignments:
        assignment_summary = {}
        for client, manager in assignments:
            if manager.get_full_name() not in assignment_summary:
                assignment_summary[manager.get_full_name()] = 0
            assignment_summary[manager.get_full_name()] += 1
        
        summary_text = ', '.join([f'{count} to {manager}' for manager, count in assignment_summary.items()])
        
        messages.success(
            request,
            f'Successfully completed bulk assignment of {len(assignments)} clients using {strategy} strategy: {summary_text}'
        )
    else:
        messages.warning(request, 'No assignments were made. All managers may be at capacity.')
    
    return redirect('users:portfolio_dashboard')


@login_required
def portfolio_analytics(request):
    """Portfolio analytics and rankings"""
    # Check if user has permission to access portfolio
    if not request.user.has_permission('portfolio', 'access'):
        messages.error(request, 'You do not have permission to access portfolio analytics.')
        return redirect('users:portfolio_dashboard')
    
    if not request.user.role in ['admin', 'team_leader']:
        messages.error(request, 'You do not have permission to view portfolio analytics.')
        return redirect('dashboard')
    
    # Get all portfolio managers with their stats
    managers = CustomUser.objects.filter(
        role__in=['loan_officer', 'team_leader'],
        status='active'
    )
    
    manager_analytics = []
    total_portfolio_value = Decimal('0')
    total_clients = 0
    total_collected = Decimal('0')
    total_disbursed = Decimal('0')
    
    for manager in managers:
        stats = manager.get_portfolio_stats()
        if stats and stats['total_clients'] > 0:
            # Add performance score calculation - convert floats to Decimal to avoid type mixing
            performance_score = (
                (stats['collection_rate'] * Decimal('0.4')) +
                (max(Decimal('0'), Decimal('100') - stats['default_rate']) * Decimal('0.3')) +
                (min(Decimal('100'), Decimal(str(stats['total_clients'])) * Decimal('2')) * Decimal('0.3'))
            )
            stats['performance_score'] = performance_score
            stats['manager'] = manager
            manager_analytics.append(stats)
            
            # Accumulate totals
            total_portfolio_value += stats['total_disbursed']
            total_clients += stats['total_clients']
            total_collected += stats['total_collected']
            total_disbursed += stats['total_disbursed']
    
    # Sort by performance score for rankings
    manager_rankings = sorted(manager_analytics, key=lambda x: x['performance_score'], reverse=True)
    
    # Calculate overall metrics
    avg_collection_rate = (total_collected / total_disbursed * 100) if total_disbursed > 0 else Decimal('0')
    
    # Prepare chart data
    chart_data = {
        'manager_names': [m['manager'].get_full_name() for m in manager_rankings[:6]],
        'collection_rates': [float(m['collection_rate']) for m in manager_rankings[:6]],
        'client_counts': [m['total_clients'] for m in manager_rankings[:6]],
        'disbursed_amounts': [float(m['total_disbursed']) for m in manager_rankings[:6]],
    }
    
    context = {
        'manager_rankings': manager_rankings,
        'total_portfolio_value': total_portfolio_value,
        'total_managers': len(manager_analytics),
        'avg_collection_rate': avg_collection_rate,
        'total_clients': total_clients,
        'chart_data': json.dumps(chart_data, cls=DecimalEncoder),
    }
    
    return render(request, 'users/portfolio_analytics.html', context)


@login_required
def reassign_client(request, client_id):
    """Reassign a client to a different portfolio manager"""
    # Check if user has permission to reassign clients
    if not request.user.has_permission('clients', 'reassign'):
        messages.error(request, 'You do not have permission to reassign clients.')
        return redirect('users:portfolio_dashboard')
    if not request.user.role in ['admin', 'team_leader']:
        messages.error(request, 'You do not have permission to reassign clients.')
        return redirect('users:portfolio_dashboard')
    
    client = get_object_or_404(CustomUser, id=client_id, role='borrower')
    
    if request.method == 'POST':
        new_manager_id = request.POST.get('new_manager_id')
        reason = request.POST.get('reason', '')
        
        try:
            new_manager = CustomUser.objects.get(id=new_manager_id, role__in=['loan_officer', 'team_leader'])
            
            # Deactivate current assignment
            if client.portfolio_manager:
                PortfolioAssignment.objects.filter(
                    client=client,
                    is_active=True
                ).update(is_active=False, unassigned_date=get_current_datetime())
            
            # Create new assignment
            client.assign_to_portfolio_manager(new_manager)
            PortfolioAssignment.objects.create(
                client=client,
                portfolio_manager=new_manager,
                assigned_by=request.user,
                reason=reason
            )
            
            messages.success(request, f'Successfully reassigned {client.get_full_name()} to {new_manager.get_full_name()}')
            
        except CustomUser.DoesNotExist:
            messages.error(request, 'Invalid portfolio manager selected.')
        except Exception as e:
            messages.error(request, f'Error reassigning client: {str(e)}')
        
        return redirect('users:portfolio_dashboard')
    
    portfolio_managers = CustomUser.objects.filter(
        role__in=['loan_officer', 'team_leader'],
        status='active',
        is_active=True
    ).exclude(id=client.portfolio_manager.id if client.portfolio_manager else None).order_by('branch__name', 'first_name')
    
    context = {
        'client': client,
        'portfolio_managers': portfolio_managers,
    }
    
    return render(request, 'users/reassign_client.html', context)


@login_required
def portfolio_performance_api(request, manager_id):
    """API endpoint for portfolio performance data"""
    manager = get_object_or_404(CustomUser, id=manager_id, role__in=['loan_officer', 'team_leader'])
    
    if not request.user.role in ['admin', 'team_leader'] and request.user != manager:
        return JsonResponse({'error': 'Permission denied'}, status=403)
    
    # Get performance trend data
    trend_data = manager.get_portfolio_performance_trend(12)  # 12 months
    
    return JsonResponse({
        'trend_data': trend_data,
        'stats': manager.get_portfolio_stats(),
    })


@login_required
def manager_clients_api(request, manager_id):
    """API endpoint to get manager's assigned clients"""
    manager = get_object_or_404(CustomUser, id=manager_id)
    
    # Get clients assigned to this manager
    clients = CustomUser.objects.filter(
        portfolio_manager=manager,
        status='active'
    ).values('id', 'first_name', 'last_name', 'email', 'business_name')
    
    clients_data = []
    for client in clients:
        clients_data.append({
            'id': str(client['id']),
            'name': f"{client['first_name']} {client['last_name']}".strip(),
            'email': client['email'] or '',
            'business_name': client['business_name'] or ''
        })
    
    return JsonResponse({
        'status': 'success',
        'clients': clients_data
    })


@login_required
def bulk_assign_clients(request):
    """Enhanced bulk assignment with validation and conflict detection"""
    # Check if user has permission to assign clients
    if not request.user.has_permission('clients', 'assign'):
        return JsonResponse({'error': 'You do not have permission to assign clients.'}, status=403)
    
    if not request.user.role in ['admin', 'team_leader']:
        return JsonResponse({'error': 'Permission denied'}, status=403)
    
    if request.method == 'POST':
        try:
            data = json.loads(request.body)
            assignment_type = data.get('type')
            client_ids = data.get('client_ids', [])
            manager_id = data.get('manager_id')
            reason = data.get('reason', '')
            
            if not client_ids:
                return JsonResponse({'error': 'No clients selected'}, status=400)
            
            # Get clients and validate they exist and are unassigned
            clients = CustomUser.objects.filter(
                id__in=client_ids, 
                role='borrower',
                status='active'
            )
            
            if clients.count() != len(client_ids):
                return JsonResponse({'error': 'Some selected clients are invalid'}, status=400)
            
            # Check for already assigned clients
            already_assigned = clients.filter(portfolio_manager__isnull=False)
            if already_assigned.exists():
                return JsonResponse({
                    'error': f'{already_assigned.count()} clients are already assigned',
                    'already_assigned': [{
                        'id': str(client.id),
                        'name': client.get_full_name(),
                        'current_manager': client.portfolio_manager.get_full_name() if client.portfolio_manager else None
                    } for client in already_assigned]
                }, status=400)
            
            unassigned_clients = clients.filter(portfolio_manager__isnull=True)
            
            if assignment_type == 'round_robin':
                # Get active portfolio managers
                managers = CustomUser.objects.filter(
                    role__in=['loan_officer', 'team_leader'],
                    status='active'
                ).annotate(
                    current_clients=Count('portfolio_clients', filter=Q(portfolio_clients__status='active'))
                ).order_by('current_clients')
                
                if not managers.exists():
                    return JsonResponse({'error': 'No available portfolio managers'}, status=400)
                
                assignments = []
                manager_index = 0
                
                for client in unassigned_clients:
                    manager = managers[manager_index % managers.count()]
                    
                    # Check capacity limit
                    max_clients = getattr(manager, 'max_clients', 100)
                    if manager.current_clients >= max_clients:
                        continue
                    
                    client.assign_to_portfolio_manager(manager)
                    PortfolioAssignment.objects.create(
                        client=client,
                        portfolio_manager=manager,
                        assigned_by=request.user,
                        reason=f'Bulk assignment (Round Robin): {reason}'
                    )
                    
                    assignments.append({
                        'client': client.get_full_name(),
                        'manager': manager.get_full_name()
                    })
                    
                    manager_index += 1
                
                return JsonResponse({
                    'success': True,
                    'message': f'Successfully assigned {len(assignments)} clients using round robin',
                    'assignments': assignments
                })
            
            elif assignment_type == 'least_loaded':
                # Assign all to the manager with least clients
                manager = CustomUser.objects.filter(
                    role__in=['loan_officer', 'team_leader'],
                    status='active'
                ).annotate(
                    current_clients=Count('portfolio_clients', filter=Q(portfolio_clients__status='active'))
                ).order_by('current_clients').first()
                
                if not manager:
                    return JsonResponse({'error': 'No available portfolio managers'}, status=400)
                
                # Check capacity
                max_clients = getattr(manager, 'max_clients', 100)
                if manager.current_clients + unassigned_clients.count() > max_clients:
                    return JsonResponse({
                        'error': f'Assignment would exceed manager capacity limit of {max_clients}'
                    }, status=400)
                
                assignments = []
                for client in unassigned_clients:
                    client.assign_to_portfolio_manager(manager)
                    PortfolioAssignment.objects.create(
                        client=client,
                        portfolio_manager=manager,
                        assigned_by=request.user,
                        reason=f'Bulk assignment (Least Loaded): {reason}'
                    )
                    assignments.append(client.get_full_name())
                
                return JsonResponse({
                    'success': True,
                    'message': f'Successfully assigned {len(assignments)} clients to {manager.get_full_name()}',
                    'manager': manager.get_full_name(),
                    'assignments': assignments
                })
            
            elif assignment_type == 'specific_manager' and manager_id:
                try:
                    manager = CustomUser.objects.get(
                        id=manager_id, 
                        role__in=['loan_officer', 'team_leader'],
                        status='active'
                    )
                    
                    # Check capacity
                    current_clients = manager.portfolio_clients.filter(status='active').count()
                    max_clients = getattr(manager, 'max_clients', 100)
                    if current_clients + unassigned_clients.count() > max_clients:
                        return JsonResponse({
                            'error': f'Assignment would exceed manager capacity limit of {max_clients}'
                        }, status=400)
                    
                    assignments = []
                    for client in unassigned_clients:
                        client.assign_to_portfolio_manager(manager)
                        PortfolioAssignment.objects.create(
                            client=client,
                            portfolio_manager=manager,
                            assigned_by=request.user,
                            reason=f'Bulk assignment (Specific Manager): {reason}'
                        )
                        assignments.append(client.get_full_name())
                    
                    return JsonResponse({
                        'success': True,
                        'message': f'Successfully assigned {len(assignments)} clients to {manager.get_full_name()}',
                        'assignments': assignments
                    })
                    
                except CustomUser.DoesNotExist:
                    return JsonResponse({'error': 'Invalid portfolio manager'}, status=400)
            
            else:
                return JsonResponse({'error': 'Invalid assignment type'}, status=400)
                
        except json.JSONDecodeError:
            return JsonResponse({'error': 'Invalid JSON data'}, status=400)
        except Exception as e:
            return JsonResponse({'error': f'Assignment failed: {str(e)}'}, status=500)
    
    return JsonResponse({'error': 'Invalid request method'}, status=405)

def calculate_performance_score(overview):
    """Calculate a performance score based on multiple metrics"""
    try:
        collection_rate = overview['performance_metrics']['collection_rate']
        default_rate = overview['performance_metrics']['default_rate']
        client_count = overview['client_metrics']['total_clients']
        loan_count = overview['loan_metrics']['active_loans']
        
        # Weighted scoring algorithm
        collection_weight = 0.4
        default_weight = 0.3
        volume_weight = 0.3
        
        # Collection rate score (0-100)
        collection_score = collection_rate
        
        # Default rate score (inverted - lower is better)
        default_score = max(0, 100 - (default_rate * 10))
        
        # Volume score (normalized based on client and loan count)
        volume_score = min(100, (client_count * 2) + (loan_count * 1.5))
        
        # Calculate weighted average
        performance_score = (
            (collection_score * collection_weight) +
            (default_score * default_weight) +
            (volume_score * volume_weight)
        )
        
        return round(performance_score, 1)
        
    except Exception as e:
        logger.error(f"Error calculating performance score: {e}")
        return 0.0


def prepare_chart_data(manager_analytics):
    """Prepare chart data for portfolio visualizations"""
    try:
        if not manager_analytics:
            return {
                'manager_names': [],
                'collection_rates': [],
                'default_rates': [],
                'client_counts': [],
                'disbursed_amounts': [],
                'performance_scores': []
            }
        
        chart_data = {
            'manager_names': [m['manager'].get_full_name() for m in manager_analytics],
            'collection_rates': [m['collection_rate'] for m in manager_analytics],
            'default_rates': [m['default_rate'] for m in manager_analytics],
            'client_counts': [m['total_clients'] for m in manager_analytics],
            'disbursed_amounts': [float(m['total_disbursed']) for m in manager_analytics],
            'performance_scores': [m['performance_score'] for m in manager_analytics],
            'outstanding_amounts': [float(m['total_outstanding']) for m in manager_analytics],
            'risk_levels': [m['risk_level'] for m in manager_analytics]
        }
        
        return chart_data
        
    except Exception as e:
        logger.error(f"Error preparing chart data: {e}")
        return {}


def get_portfolio_risk_alerts(manager_analytics):
    """Generate risk alerts for portfolio management"""
    try:
        alerts = []
        
        for manager_data in manager_analytics:
            manager = manager_data['manager']
            
            # High default rate alert
            if manager_data['default_rate'] > 10:
                alerts.append({
                    'type': 'danger',
                    'title': 'High Default Rate',
                    'message': f"{manager.get_full_name()} has a default rate of {manager_data['default_rate']:.1f}%",
                    'manager_id': manager.id,
                    'priority': 'high'
                })
            
            # Low collection rate alert
            if manager_data['collection_rate'] < 70:
                alerts.append({
                    'type': 'warning',
                    'title': 'Low Collection Rate',
                    'message': f"{manager.get_full_name()} has a collection rate of {manager_data['collection_rate']:.1f}%",
                    'manager_id': manager.id,
                    'priority': 'medium'
                })
            
            # High risk level alert
            if manager_data['risk_level'] == 'high':
                alerts.append({
                    'type': 'danger',
                    'title': 'High Risk Portfolio',
                    'message': f"{manager.get_full_name()}'s portfolio is classified as high risk",
                    'manager_id': manager.id,
                    'priority': 'high'
                })
            
            # Large portfolio without proportional performance
            if manager_data['total_clients'] > 50 and manager_data['performance_score'] < 60:
                alerts.append({
                    'type': 'info',
                    'title': 'Large Portfolio Performance',
                    'message': f"{manager.get_full_name()} manages {manager_data['total_clients']} clients but has low performance score",
                    'manager_id': manager.id,
                    'priority': 'low'
                })
        
        # Sort alerts by priority
        priority_order = {'high': 3, 'medium': 2, 'low': 1}
        alerts.sort(key=lambda x: priority_order.get(x['priority'], 0), reverse=True)
        
        return alerts[:10]  # Return top 10 alerts
        
    except Exception as e:
        logger.error(f"Error generating risk alerts: {e}")
        return []


def get_performance_trends(portfolio_managers, analytics_service):
    """Get performance trends for portfolio managers"""
    try:
        trends_data = []
        
        for manager in portfolio_managers:
            try:
                # Get performance trends from analytics service
                trends = analytics_service.get_performance_trends(str(manager.id), 'monthly')
                
                if 'error' not in trends:
                    trends_data.append({
                        'manager': manager,
                        'trends': trends,
                        'current_month': trends.get('current_period', {}),
                        'previous_month': trends.get('previous_period', {}),
                        'growth_rate': trends.get('growth_metrics', {}).get('collection_rate_change', 0)
                    })
                    
            except Exception as e:
                logger.error(f"Error getting trends for manager {manager.id}: {e}")
                continue
        
        return trends_data
        
    except Exception as e:
        logger.error(f"Error getting performance trends: {e}")
        return []


def get_portfolio_comparison_data(manager_analytics):
    """Generate portfolio comparison data for benchmarking"""
    try:
        if not manager_analytics:
            return {}
        
        # Calculate industry benchmarks
        collection_rates = [m['collection_rate'] for m in manager_analytics]
        default_rates = [m['default_rate'] for m in manager_analytics]
        client_counts = [m['total_clients'] for m in manager_analytics]
        portfolio_values = [m['total_outstanding'] for m in manager_analytics]
        
        comparison_data = {
            'industry_benchmarks': {
                'avg_collection_rate': sum(collection_rates) / len(collection_rates),
                'avg_default_rate': sum(default_rates) / len(default_rates),
                'avg_client_count': sum(client_counts) / len(client_counts),
                'avg_portfolio_value': sum(portfolio_values) / len(portfolio_values),
                'top_quartile_collection': sorted(collection_rates, reverse=True)[len(collection_rates)//4] if len(collection_rates) > 4 else max(collection_rates),
                'bottom_quartile_collection': sorted(collection_rates)[len(collection_rates)//4] if len(collection_rates) > 4 else min(collection_rates),
            },
            'performance_distribution': {
                'excellent': len([r for r in collection_rates if r >= 95]),
                'good': len([r for r in collection_rates if 85 <= r < 95]),
                'average': len([r for r in collection_rates if 70 <= r < 85]),
                'poor': len([r for r in collection_rates if r < 70]),
            },
            'risk_distribution': {
                'low_risk': len([r for r in default_rates if r <= 2]),
                'medium_risk': len([r for r in default_rates if 2 < r <= 5]),
                'high_risk': len([r for r in default_rates if r > 5]),
            }
        }
        
        return comparison_data
        
    except Exception as e:
        logger.error(f"Error generating portfolio comparison data: {e}")
        return {}


def get_benchmarking_data(manager_analytics, analytics_service):
    """Generate benchmarking data for portfolio performance analysis"""
    try:
        if not manager_analytics:
            return {}
        
        # Calculate performance metrics
        performance_scores = [m['performance_score'] for m in manager_analytics]
        collection_rates = [m['collection_rate'] for m in manager_analytics]
        
        # Generate benchmarking insights
        benchmarking_data = {
            'top_performers': [
                m for m in manager_analytics 
                if m['performance_score'] >= sorted(performance_scores, reverse=True)[min(2, len(performance_scores)-1)]
            ][:3],
            'improvement_opportunities': [
                m for m in manager_analytics 
                if m['collection_rate'] < sum(collection_rates) / len(collection_rates)
            ],
            'risk_managers': [
                m for m in manager_analytics 
                if m['risk_level'] == 'high' or m['default_rate'] > 5
            ],
            'growth_leaders': [
                m for m in manager_analytics 
                if m['total_clients'] > sum([ma['total_clients'] for ma in manager_analytics]) / len(manager_analytics)
            ],
            'efficiency_metrics': {
                'avg_clients_per_manager': sum([m['total_clients'] for m in manager_analytics]) / len(manager_analytics),
                'avg_loans_per_manager': sum([m['active_loans'] for m in manager_analytics]) / len(manager_analytics),
                'avg_portfolio_per_manager': sum([m['total_outstanding'] for m in manager_analytics]) / len(manager_analytics),
            }
        }
        
        return benchmarking_data
        
    except Exception as e:
        logger.error(f"Error generating benchmarking data: {e}")
        return {}


@login_required
def client_performance_ranking(request):
    """
    Detailed client performance ranking and analysis view
    """
    # Check if user has permission to access portfolio
    if not request.user.has_permission('portfolio', 'access'):
        messages.error(request, 'You do not have permission to access portfolio analytics.')
        return redirect('users:portfolio_dashboard')
    try:
        # Initialize analytics service
        analytics_service = PortfolioAnalyticsService()
        
        # Get current user and check permissions
        user = request.user
        if user.role not in ['admin', 'team_leader', 'loan_officer']:
            return render(request, 'errors/403.html', {
                'message': 'You do not have permission to access client performance analytics.'
            })
        
        # Get date range from request
        date_range_days = int(request.GET.get('days', 30))
        end_date = timezone.now().date()
        start_date = end_date - timedelta(days=date_range_days)
        date_range = (start_date.isoformat(), end_date.isoformat())
        
        # Get client performance data
        client_performance_data = []
        
        # Get clients based on user role
        if user.role == 'admin':
            # Admin sees ALL clients
            clients = CustomUser.objects.filter(
                role='borrower',
                status='active'
            ).select_related('portfolio_manager')
        elif user.role == 'team_leader':
            # Team leader sees clients in their branch
            clients = CustomUser.objects.filter(
                role='borrower',
                status='active',
                branch=user.branch
            ).select_related('portfolio_manager')
        else:
            # Loan officer sees only their assigned clients
            clients = CustomUser.objects.filter(
                portfolio_manager=user,
                role='borrower',
                status='active'
            ).select_related('portfolio_manager')
        
        # Process each client
        for client in clients:
            try:
                manager = client.portfolio_manager
                
                # Get client's loans
                client_loans = Loan.objects.filter(borrower=client)
                total_loans = client_loans.count()
                active_loans = client_loans.filter(status='active').count()
                
                # Calculate financial metrics
                total_borrowed = sum(loan.total_amount for loan in client_loans)
                total_repaid = sum(getattr(loan, '_amount_paid_cache', 0) or 0 for loan in client_loans)
                outstanding_balance = sum(
                    loan.total_amount - (getattr(loan, '_amount_paid_cache', 0) or 0) 
                    for loan in client_loans.filter(status='active')
                )
                
                # Calculate repayment rate
                repayment_rate = (total_repaid / total_borrowed * 100) if total_borrowed > 0 else 0
                
                # Get last payment date
                from loans.models import Repayment
                last_repayment = Repayment.objects.filter(loan__borrower=client).order_by('-payment_date').first()
                last_payment_date = last_repayment.payment_date if last_repayment else None
                days_since_payment = (timezone.now().date() - last_payment_date).days if last_payment_date else 999
                
                # Calculate default history (loans that defaulted)
                default_history = client_loans.filter(status='defaulted').count()
                
                # Calculate client score (simple algorithm)
                score = 100
                if default_history > 0:
                    score -= (default_history * 10)
                if days_since_payment > 30:
                    score -= 20
                elif days_since_payment > 60:
                    score -= 40
                if repayment_rate < 50:
                    score -= 30
                elif repayment_rate < 70:
                    score -= 15
                client_score = max(0, min(100, score))
                
                # Determine risk category
                if client_score >= 85:
                    risk_category = 'low'
                elif client_score >= 70:
                    risk_category = 'medium'
                else:
                    risk_category = 'high'
                
                client_performance_data.append({
                    'manager': manager,
                    'manager_name': manager.get_full_name() if manager else 'Unassigned',
                    'client_id': str(client.id),
                    'client_name': client.get_full_name(),
                    'total_loans': total_loans,
                    'active_loans': active_loans,
                    'total_borrowed': float(total_borrowed),
                    'total_repaid': float(total_repaid),
                    'outstanding_balance': float(outstanding_balance),
                    'repayment_rate': float(repayment_rate),
                    'default_history': default_history,
                    'client_score': client_score,
                    'risk_category': risk_category,
                    'last_payment_date': last_payment_date,
                    'days_since_payment': days_since_payment
                })
                    
            except Exception as e:
                logger.error(f"Error getting client performance for client {client.id}: {e}")
                continue
        
        # Sort clients by performance score
        client_performance_data.sort(key=lambda x: x['client_score'], reverse=True)
        
        # Calculate performance categories
        performance_categories = {
            'excellent': [c for c in client_performance_data if c['client_score'] >= 85],
            'good': [c for c in client_performance_data if 70 <= c['client_score'] < 85],
            'average': [c for c in client_performance_data if 50 <= c['client_score'] < 70],
            'poor': [c for c in client_performance_data if c['client_score'] < 50]
        }
        
        # Calculate summary statistics
        total_clients = len(client_performance_data)
        avg_client_score = sum(c['client_score'] for c in client_performance_data) / total_clients if total_clients > 0 else 0
        total_outstanding = sum(c['outstanding_balance'] for c in client_performance_data)
        avg_repayment_rate = sum(c['repayment_rate'] for c in client_performance_data) / total_clients if total_clients > 0 else 0
        
        # Get top and bottom performers
        top_performers = client_performance_data[:10]
        bottom_performers = client_performance_data[-10:] if len(client_performance_data) > 10 else []
        
        # Prepare chart data for client performance visualization
        client_chart_data = {
            'client_names': [c['client_name'][:20] + '...' if len(c['client_name']) > 20 else c['client_name'] for c in top_performers],
            'client_scores': [c['client_score'] for c in top_performers],
            'repayment_rates': [c['repayment_rate'] for c in top_performers],
            'outstanding_balances': [float(c['outstanding_balance']) for c in top_performers],
            'risk_categories': [c['risk_category'] for c in top_performers]
        }
        
        context = {
            'client_performance_data': client_performance_data[:50],  # Limit to top 50 for display
            'performance_categories': performance_categories,
            'total_clients': total_clients,
            'avg_client_score': avg_client_score,
            'total_outstanding': total_outstanding,
            'avg_repayment_rate': avg_repayment_rate,
            'top_performers': top_performers,
            'bottom_performers': bottom_performers,
            'client_chart_data': json.dumps(client_chart_data),
            'date_range_days': date_range_days,
        }
        
        return render(request, 'users/client_performance_ranking.html', context)
        
    except Exception as e:
        logger.error(f"Error in client performance ranking view: {e}")
        return render(request, 'errors/500.html', {
            'message': 'An error occurred while loading client performance data.'
        })


@login_required 
def portfolio_benchmarking_dashboard(request):
    """
    Portfolio benchmarking dashboard with comparison features
    """
    # Check if user has permission to access portfolio
    if not request.user.has_permission('portfolio', 'access'):
        messages.error(request, 'You do not have permission to access portfolio analytics.')
        return redirect('users:portfolio_dashboard')
    try:
        # Initialize analytics service
        analytics_service = PortfolioAnalyticsService()
        
        # Get current user and check permissions
        user = request.user
        if user.role not in ['admin', 'team_leader']:
            return render(request, 'errors/403.html', {
                'message': 'You do not have permission to access benchmarking dashboard.'
            })
        
        # Get comparison parameters
        comparison_type = request.GET.get('type', 'branch')  # branch, role, period
        period = request.GET.get('period', 'monthly')  # monthly, quarterly, yearly
        
        # Get portfolio managers for comparison
        if user.role == 'admin':
            portfolio_managers = CustomUser.objects.filter(
                role__in=['loan_officer', 'team_leader'],
                status='active'
            ).select_related('branch')
        else:
            portfolio_managers = CustomUser.objects.filter(
                role__in=['loan_officer', 'team_leader'],
                branch=user.branch,
                status='active'
            ).select_related('branch')
        
        # Generate benchmarking data
        benchmarking_results = []
        
        for manager in portfolio_managers:
            try:
                # Get portfolio overview
                overview = analytics_service.get_portfolio_overview(
                    str(manager.id), 
                    get_date_range_for_period(period)
                )
                
                if 'error' not in overview:
                    benchmarking_results.append({
                        'manager': manager,
                        'overview': overview,
                        'performance_score': calculate_performance_score(overview),
                        'efficiency_ratio': calculate_efficiency_ratio(overview),
                        'growth_rate': calculate_growth_rate(overview),
                        'risk_score': calculate_risk_score(overview)
                    })
                    
            except Exception as e:
                logger.error(f"Error getting benchmarking data for manager {manager.id}: {e}")
                continue
        
        # Generate comparison insights
        comparison_insights = generate_comparison_insights(benchmarking_results, comparison_type)
        
        # Calculate industry benchmarks
        industry_benchmarks = calculate_industry_benchmarks(benchmarking_results)
        
        # Prepare visualization data
        benchmark_chart_data = prepare_benchmark_chart_data(benchmarking_results, comparison_type)
        
        context = {
            'benchmarking_results': benchmarking_results,
            'comparison_insights': comparison_insights,
            'industry_benchmarks': industry_benchmarks,
            'benchmark_chart_data': json.dumps(benchmark_chart_data),
            'comparison_type': comparison_type,
            'period': period,
        }
        
        return render(request, 'users/portfolio_benchmarking_dashboard.html', context)
        
    except Exception as e:
        logger.error(f"Error in portfolio benchmarking dashboard: {e}")
        return render(request, 'errors/500.html', {
            'message': 'An error occurred while loading benchmarking data.'
        })


def get_date_range_for_period(period):
    """Get date range tuple for the specified period"""
    end_date = timezone.now().date()
    
    if period == 'monthly':
        start_date = end_date - timedelta(days=30)
    elif period == 'quarterly':
        start_date = end_date - timedelta(days=90)
    elif period == 'yearly':
        start_date = end_date - timedelta(days=365)
    else:
        start_date = end_date - timedelta(days=30)
    
    return (start_date.isoformat(), end_date.isoformat())


def calculate_efficiency_ratio(overview):
    """Calculate efficiency ratio based on portfolio metrics"""
    try:
        total_clients = overview['client_metrics']['total_clients']
        total_loans = overview['loan_metrics']['active_loans']
        collection_rate = overview['performance_metrics']['collection_rate']
        
        if total_clients == 0:
            return 0
        
        # Efficiency = (loans per client * collection rate) / 100
        loans_per_client = total_loans / total_clients if total_clients > 0 else 0
        efficiency_ratio = (loans_per_client * collection_rate) / 100
        
        return round(efficiency_ratio, 2)
        
    except Exception as e:
        logger.error(f"Error calculating efficiency ratio: {e}")
        return 0


def calculate_growth_rate(overview):
    """Calculate growth rate based on portfolio metrics"""
    try:
        # This would typically compare current vs previous period
        # For now, we'll use a simplified calculation
        total_disbursed = overview['loan_metrics']['total_disbursed']
        total_clients = overview['client_metrics']['total_clients']
        
        if total_clients == 0:
            return 0
        
        # Growth rate approximation based on disbursement per client
        growth_rate = (total_disbursed / total_clients) / 1000  # Simplified metric
        
        return round(min(growth_rate, 100), 2)  # Cap at 100%
        
    except Exception as e:
        logger.error(f"Error calculating growth rate: {e}")
        return 0


def calculate_risk_score(overview):
    """Calculate risk score based on portfolio metrics"""
    try:
        default_rate = overview['performance_metrics']['default_rate']
        collection_rate = overview['performance_metrics']['collection_rate']
        
        # Risk score = default rate weighted by collection efficiency
        risk_multiplier = (100 - collection_rate) / 100
        risk_score = default_rate * (1 + risk_multiplier)
        
        return round(min(risk_score, 100), 2)  # Cap at 100
        
    except Exception as e:
        logger.error(f"Error calculating risk score: {e}")
        return 0


def generate_comparison_insights(benchmarking_results, comparison_type):
    """Generate insights based on benchmarking comparison"""
    try:
        if not benchmarking_results:
            return {}
        
        insights = {
            'top_performer': max(benchmarking_results, key=lambda x: x['performance_score']),
            'most_efficient': max(benchmarking_results, key=lambda x: x['efficiency_ratio']),
            'fastest_growing': max(benchmarking_results, key=lambda x: x['growth_rate']),
            'lowest_risk': min(benchmarking_results, key=lambda x: x['risk_score']),
            'needs_attention': [r for r in benchmarking_results if r['performance_score'] < 50],
            'performance_leaders': [r for r in benchmarking_results if r['performance_score'] >= 80]
        }
        
        return insights
        
    except Exception as e:
        logger.error(f"Error generating comparison insights: {e}")
        return {}


def calculate_industry_benchmarks(benchmarking_results):
    """Calculate industry benchmark metrics"""
    try:
        if not benchmarking_results:
            return {}
        
        performance_scores = [r['performance_score'] for r in benchmarking_results]
        efficiency_ratios = [r['efficiency_ratio'] for r in benchmarking_results]
        growth_rates = [r['growth_rate'] for r in benchmarking_results]
        risk_scores = [r['risk_score'] for r in benchmarking_results]
        
        benchmarks = {
            'avg_performance_score': sum(performance_scores) / len(performance_scores),
            'avg_efficiency_ratio': sum(efficiency_ratios) / len(efficiency_ratios),
            'avg_growth_rate': sum(growth_rates) / len(growth_rates),
            'avg_risk_score': sum(risk_scores) / len(risk_scores),
            'top_quartile_performance': sorted(performance_scores, reverse=True)[len(performance_scores)//4] if len(performance_scores) > 4 else max(performance_scores),
            'bottom_quartile_performance': sorted(performance_scores)[len(performance_scores)//4] if len(performance_scores) > 4 else min(performance_scores)
        }
        
        return benchmarks
        
    except Exception as e:
        logger.error(f"Error calculating industry benchmarks: {e}")
        return {}


def prepare_benchmark_chart_data(benchmarking_results, comparison_type):
    """Prepare chart data for benchmarking visualizations"""
    try:
        if not benchmarking_results:
            return {}
        
        chart_data = {
            'manager_names': [r['manager'].get_full_name() for r in benchmarking_results],
            'performance_scores': [r['performance_score'] for r in benchmarking_results],
            'efficiency_ratios': [r['efficiency_ratio'] for r in benchmarking_results],
            'growth_rates': [r['growth_rate'] for r in benchmarking_results],
            'risk_scores': [r['risk_score'] for r in benchmarking_results],
            'branches': [r['manager'].branch.name if r['manager'].branch else 'No Branch' for r in benchmarking_results],
            'roles': [r['manager'].get_role_display() for r in benchmarking_results]
        }
        
        return chart_data
        
    except Exception as e:
        logger.error(f"Error preparing benchmark chart data: {e}")
        return {}