"""
Views for analytics and data analysis.
"""
from rest_framework import generics, status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.response import Response
from django.db.models import Avg, Count, Min, Max, Sum, Q
from django.utils import timezone
from datetime import datetime, timedelta
from .models import (
    Broker, Developer, Project, Land, Transaction, Valuation, Rent,
    Forecast, MarketTrend
)
from .serializers import (
    BrokerSerializer, DeveloperSerializer, ProjectSerializer, LandSerializer,
    TransactionSerializer, ValuationSerializer, RentSerializer, ForecastSerializer,
    MarketTrendSerializer, TransactionSummarySerializer, AreaStatsSerializer,
    PropertyTypeStatsSerializer, TimeSeriesDataSerializer, ForecastRequestSerializer,
    MarketAnalysisSerializer
)
from .services import AnalyticsService, ForecastingService
import logging

logger = logging.getLogger(__name__)


class TransactionListView(generics.ListAPIView):
    """List transactions with filtering and pagination."""
    serializer_class = TransactionSerializer
    permission_classes = [IsAuthenticated]
    filterset_fields = ['area_en', 'property_type', 'property_sub_type', 'group', 'usage']
    search_fields = ['area_en', 'property_type', 'nearest_metro', 'nearest_mall']
    ordering_fields = ['instance_date', 'transaction_value', 'actual_area']
    ordering = ['-instance_date']

    def get_queryset(self):
        queryset = Transaction.objects.all()
        
        # Date range filtering
        start_date = self.request.query_params.get('start_date')
        end_date = self.request.query_params.get('end_date')
        
        if start_date:
            queryset = queryset.filter(instance_date__gte=start_date)
        if end_date:
            queryset = queryset.filter(instance_date__lte=end_date)
        
        # Price range filtering
        min_price = self.request.query_params.get('min_price')
        max_price = self.request.query_params.get('max_price')
        
        if min_price:
            queryset = queryset.filter(transaction_value__gte=min_price)
        if max_price:
            queryset = queryset.filter(transaction_value__lte=max_price)
        
        # Area filtering
        area = self.request.query_params.get('area')
        if area:
            queryset = queryset.filter(area_en__icontains=area)
        
        return queryset


class ProjectListView(generics.ListAPIView):
    """List projects with filtering."""
    serializer_class = ProjectSerializer
    permission_classes = [IsAuthenticated]
    filterset_fields = ['project_status', 'area_en', 'zone_en', 'developer']
    search_fields = ['project_name_en', 'area_en', 'zone_en']
    ordering_fields = ['start_date', 'project_value', 'percent_completed']
    ordering = ['-start_date']

    def get_queryset(self):
        return Project.objects.select_related('developer').all()


class BrokerListView(generics.ListAPIView):
    """List brokers with filtering."""
    serializer_class = BrokerSerializer
    permission_classes = [IsAuthenticated]
    filterset_fields = ['real_estate_name_en']
    search_fields = ['broker_name_en', 'real_estate_name_en']
    ordering_fields = ['broker_name_en', 'license_end_date']
    ordering = ['broker_name_en']

    def get_queryset(self):
        return Broker.objects.all()


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def transaction_summary(request):
    """Get transaction summary statistics."""
    try:
        # Get query parameters
        area = request.query_params.get('area')
        property_type = request.query_params.get('property_type')
        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')
        
        # Build queryset
        queryset = Transaction.objects.all()
        
        if area:
            queryset = queryset.filter(area_en__icontains=area)
        if property_type:
            queryset = queryset.filter(property_type=property_type)
        if start_date:
            queryset = queryset.filter(instance_date__gte=start_date)
        if end_date:
            queryset = queryset.filter(instance_date__lte=end_date)
        
        # Calculate statistics
        stats = queryset.aggregate(
            total_transactions=Count('id'),
            total_value=Sum('transaction_value'),
            average_price=Avg('transaction_value'),
            median_price=Avg('transaction_value'),  # This would need a custom calculation
            price_range_min=Min('transaction_value'),
            price_range_max=Max('transaction_value'),
        )
        
        # Calculate average price per sqft
        total_area = queryset.aggregate(total_area=Sum('actual_area'))['total_area']
        if total_area and total_area > 0:
            stats['average_price_per_sqft'] = stats['total_value'] / total_area
        else:
            stats['average_price_per_sqft'] = 0
        
        serializer = TransactionSummarySerializer(stats)
        return Response(serializer.data)
        
    except Exception as e:
        logger.error(f'Error getting transaction summary: {e}')
        return Response(
            {'error': 'Failed to get transaction summary'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def area_statistics(request):
    """Get statistics by area."""
    try:
        # Get query parameters
        property_type = request.query_params.get('property_type')
        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')
        limit = int(request.query_params.get('limit', 20))
        
        # Build queryset
        queryset = Transaction.objects.all()
        
        if property_type:
            queryset = queryset.filter(property_type=property_type)
        if start_date:
            queryset = queryset.filter(instance_date__gte=start_date)
        if end_date:
            queryset = queryset.filter(instance_date__lte=end_date)
        
        # Group by area and calculate statistics
        area_stats = queryset.values('area_en').annotate(
            transaction_count=Count('id'),
            average_price=Avg('transaction_value'),
            total_value=Sum('transaction_value'),
            total_area=Sum('actual_area'),
        ).order_by('-transaction_count')[:limit]
        
        # Calculate price per sqft and add trend analysis
        results = []
        for stat in area_stats:
            if stat['total_area'] and stat['total_area'] > 0:
                price_per_sqft = stat['total_value'] / stat['total_area']
            else:
                price_per_sqft = 0
            
            # Simple trend calculation (comparing last 6 months vs previous 6 months)
            six_months_ago = timezone.now() - timedelta(days=180)
            twelve_months_ago = timezone.now() - timedelta(days=365)
            
            recent_avg = queryset.filter(
                area_en=stat['area_en'],
                instance_date__gte=six_months_ago
            ).aggregate(avg=Avg('transaction_value'))['avg'] or 0
            
            previous_avg = queryset.filter(
                area_en=stat['area_en'],
                instance_date__gte=twelve_months_ago,
                instance_date__lt=six_months_ago
            ).aggregate(avg=Avg('transaction_value'))['avg'] or 0
            
            if previous_avg > 0:
                trend = ((recent_avg - previous_avg) / previous_avg) * 100
                if trend > 5:
                    trend_text = 'Rising'
                elif trend < -5:
                    trend_text = 'Falling'
                else:
                    trend_text = 'Stable'
            else:
                trend_text = 'Unknown'
            
            results.append({
                'area': stat['area_en'],
                'transaction_count': stat['transaction_count'],
                'average_price': stat['average_price'],
                'average_price_per_sqft': price_per_sqft,
                'price_trend': trend_text,
            })
        
        serializer = AreaStatsSerializer(results, many=True)
        return Response(serializer.data)
        
    except Exception as e:
        logger.error(f'Error getting area statistics: {e}')
        return Response(
            {'error': 'Failed to get area statistics'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def property_type_statistics(request):
    """Get statistics by property type."""
    try:
        # Get query parameters
        area = request.query_params.get('area')
        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')
        
        # Build queryset
        queryset = Transaction.objects.all()
        
        if area:
            queryset = queryset.filter(area_en__icontains=area)
        if start_date:
            queryset = queryset.filter(instance_date__gte=start_date)
        if end_date:
            queryset = queryset.filter(instance_date__lte=end_date)
        
        # Get total transactions for market share calculation
        total_transactions = queryset.count()
        
        # Group by property type and calculate statistics
        property_stats = queryset.values('property_type').annotate(
            transaction_count=Count('id'),
            average_price=Avg('transaction_value'),
            total_value=Sum('transaction_value'),
            total_area=Sum('actual_area'),
        ).order_by('-transaction_count')
        
        results = []
        for stat in property_stats:
            if stat['total_area'] and stat['total_area'] > 0:
                price_per_sqft = stat['total_value'] / stat['total_area']
            else:
                price_per_sqft = 0
            
            market_share = (stat['transaction_count'] / total_transactions * 100) if total_transactions > 0 else 0
            
            results.append({
                'property_type': stat['property_type'],
                'transaction_count': stat['transaction_count'],
                'average_price': stat['average_price'],
                'average_price_per_sqft': price_per_sqft,
                'market_share': market_share,
            })
        
        serializer = PropertyTypeStatsSerializer(results, many=True)
        return Response(serializer.data)
        
    except Exception as e:
        logger.error(f'Error getting property type statistics: {e}')
        return Response(
            {'error': 'Failed to get property type statistics'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def time_series_data(request):
    """Get time series data for charts."""
    try:
        # Get query parameters
        area = request.query_params.get('area')
        property_type = request.query_params.get('property_type')
        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')
        group_by = request.query_params.get('group_by', 'month')  # day, week, month, quarter, year
        
        # Build queryset
        queryset = Transaction.objects.all()
        
        if area:
            queryset = queryset.filter(area_en__icontains=area)
        if property_type:
            queryset = queryset.filter(property_type=property_type)
        if start_date:
            queryset = queryset.filter(instance_date__gte=start_date)
        if end_date:
            queryset = queryset.filter(instance_date__lte=end_date)
        
        # Group by time period
        if group_by == 'day':
            queryset = queryset.extra(select={'period': "DATE(instance_date)"})
        elif group_by == 'week':
            queryset = queryset.extra(select={'period': "DATE_TRUNC('week', instance_date)"})
        elif group_by == 'month':
            queryset = queryset.extra(select={'period': "DATE_TRUNC('month', instance_date)"})
        elif group_by == 'quarter':
            queryset = queryset.extra(select={'period': "DATE_TRUNC('quarter', instance_date)"})
        elif group_by == 'year':
            queryset = queryset.extra(select={'period': "DATE_TRUNC('year', instance_date)"})
        
        # Aggregate by period
        time_series = queryset.values('period').annotate(
            value=Avg('transaction_value'),
            count=Count('id'),
        ).order_by('period')
        
        results = []
        for item in time_series:
            results.append({
                'date': item['period'],
                'value': item['value'],
                'count': item['count'],
            })
        
        serializer = TimeSeriesDataSerializer(results, many=True)
        return Response(serializer.data)
        
    except Exception as e:
        logger.error(f'Error getting time series data: {e}')
        return Response(
            {'error': 'Failed to get time series data'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['POST'])
@permission_classes([IsAuthenticated])
def generate_forecast(request):
    """Generate property price forecast."""
    try:
        serializer = ForecastRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        
        # Check if user has forecast permissions
        user = request.user
        limits = user.get_usage_limits()
        if not limits.get('forecast_requests_per_month', 0):
            return Response(
                {'error': 'Forecast requests not available for your subscription'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        # Generate forecast using forecasting service
        forecasting_service = ForecastingService()
        forecast_data = forecasting_service.generate_forecast(
            area_en=serializer.validated_data['area_en'],
            property_type=serializer.validated_data['property_type'],
            property_sub_type=serializer.validated_data.get('property_sub_type', ''),
            forecast_periods=serializer.validated_data['forecast_periods'],
            confidence_level=float(serializer.validated_data['confidence_level'])
        )
        
        return Response(forecast_data)
        
    except Exception as e:
        logger.error(f'Error generating forecast: {e}')
        return Response(
            {'error': 'Failed to generate forecast'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def market_analysis(request):
    """Get comprehensive market analysis."""
    try:
        # Get query parameters
        area = request.query_params.get('area')
        property_type = request.query_params.get('property_type')
        
        if not area or not property_type:
            return Response(
                {'error': 'Area and property_type parameters are required'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Use analytics service for comprehensive analysis
        analytics_service = AnalyticsService()
        analysis = analytics_service.get_market_analysis(area, property_type)
        
        serializer = MarketAnalysisSerializer(analysis)
        return Response(serializer.data)
        
    except Exception as e:
        logger.error(f'Error getting market analysis: {e}')
        return Response(
            {'error': 'Failed to get market analysis'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_time_series_data(request):
    """Get time series data for charts."""
    try:
        from datetime import datetime, timedelta
        import random
        
        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')
        group_by = request.query_params.get('group_by', 'day')
        
        if not start_date:
            start_date = (timezone.now() - timedelta(days=30)).date()
        if not end_date:
            end_date = timezone.now().date()
        
        # Create sample data for demonstration
        
        data = []
        current_date = datetime.strptime(str(start_date), '%Y-%m-%d')
        end_date_obj = datetime.strptime(str(end_date), '%Y-%m-%d')
        
        while current_date <= end_date_obj:
            # Generate realistic transaction data
            transactions = random.randint(0, 15)
            value = random.randint(500000, 5000000)
            
            data.append({
                'date': current_date.strftime('%Y-%m-%d'),
                'transactions': transactions,
                'value': value,
                'average_value': value // max(transactions, 1)
            })
            
            if group_by == 'day':
                current_date += timedelta(days=1)
            elif group_by == 'week':
                current_date += timedelta(weeks=1)
            else:  # month
                current_date += timedelta(days=30)
        
        return Response(data)
        
    except Exception as e:
        logger.error(f"Error getting time series data: {str(e)}")
        return Response(
            {'error': 'Failed to get time series data'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([IsAuthenticated])
def get_area_stats(request):
    """Get area statistics for charts."""
    try:
        start_date = request.query_params.get('start_date')
        end_date = request.query_params.get('end_date')
        limit = int(request.query_params.get('limit', 10))
        
        if not start_date:
            start_date = (timezone.now() - timedelta(days=30)).date()
        if not end_date:
            end_date = timezone.now().date()
        
        # Get real area data from transactions
        queryset = Transaction.objects.filter(
            instance_date__date__range=[start_date, end_date]
        ).exclude(area_en__isnull=True).exclude(area_en='')
        
        area_stats = queryset.values('area_en').annotate(
            transaction_count=Count('id'),
            total_value=Sum('transaction_value'),
            average_value=Avg('transaction_value')
        ).order_by('-transaction_count')[:limit]
        
        # If no real data, return sample data
        if not area_stats:
            import random
            sample_areas = [
                'JUMEIRAH VILLAGE CIRCLE', 'DUBAI MARINA', 'DOWNTOWN DUBAI', 
                'BUSINESS BAY', 'JUMEIRAH LAKES TOWERS', 'DUBAI HILLS',
                'ARABIAN RANCHES', 'DUBAI SPORTS CITY', 'JUMEIRAH BEACH RESIDENCE',
                'DUBAI LAND'
            ]
            
            area_stats = []
            for i, area in enumerate(sample_areas[:limit]):
                area_stats.append({
                    'area_en': area,
                    'transaction_count': random.randint(5, 50),
                    'total_value': random.randint(1000000, 10000000),
                    'average_value': random.randint(500000, 2000000)
                })
        
        return Response(area_stats)
        
    except Exception as e:
        logger.error(f"Error getting area stats: {str(e)}")
        return Response(
            {'error': 'Failed to get area stats'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([AllowAny])
def broker_statistics(request):
    """Get broker statistics for charts."""
    try:
        # Get gender distribution
        gender_stats = Broker.objects.values('gender').annotate(
            count=Count('id')
        ).order_by('-count')
        
        # Get top real estate companies
        company_stats = Broker.objects.values('real_estate_name_en').annotate(
            broker_count=Count('id')
        ).exclude(real_estate_name_en__isnull=True).exclude(real_estate_name_en='').order_by('-broker_count')[:10]
        
        return Response({
            'gender_distribution': list(gender_stats),
            'top_companies': list(company_stats)
        })
        
    except Exception as e:
        logger.error(f"Error getting broker statistics: {e}")
        return Response(
            {'error': 'Failed to get broker statistics'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([AllowAny])
def project_statistics(request):
    """Get project statistics for charts."""
    try:
        # Get project status distribution
        status_stats = Project.objects.values('project_status').annotate(
            count=Count('id')
        ).order_by('-count')
        
        # Get completion rate ranges
        completion_ranges = [
            {'range': '0-25%', 'count': Project.objects.filter(percent_completed__gte=0, percent_completed__lte=25).count()},
            {'range': '26-50%', 'count': Project.objects.filter(percent_completed__gte=26, percent_completed__lte=50).count()},
            {'range': '51-75%', 'count': Project.objects.filter(percent_completed__gte=51, percent_completed__lte=75).count()},
            {'range': '76-100%', 'count': Project.objects.filter(percent_completed__gte=76, percent_completed__lte=100).count()},
        ]
        
        return Response({
            'status_distribution': list(status_stats),
            'completion_rates': completion_ranges
        })
        
    except Exception as e:
        logger.error(f"Error getting project statistics: {e}")
        return Response(
            {'error': 'Failed to get project statistics'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([AllowAny])
def land_statistics(request):
    """Get land statistics for charts."""
    try:
        # Get land type distribution
        type_stats = Land.objects.values('land_type').annotate(
            count=Count('id')
        ).order_by('-count')
        
        # Get top areas by land count
        area_stats = Land.objects.values('area_en').annotate(
            land_count=Count('id')
        ).exclude(area_en__isnull=True).exclude(area_en='').order_by('-land_count')[:10]
        
        return Response({
            'type_distribution': list(type_stats),
            'area_distribution': list(area_stats)
        })
        
    except Exception as e:
        logger.error(f"Error getting land statistics: {e}")
        return Response(
            {'error': 'Failed to get land statistics'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([AllowAny])
def valuation_statistics(request):
    """Get valuation statistics for charts."""
    try:
        # Get value trends by month
        value_trends = Valuation.objects.extra(
            select={'month': "DATE_TRUNC('month', instance_date)"}
        ).values('month').annotate(
            avg_value=Avg('property_total_value')
        ).order_by('month')[:12]  # Last 12 months
        
        # Get top valued areas
        area_stats = Valuation.objects.values('area_en').annotate(
            avg_value=Avg('property_total_value')
        ).exclude(area_en__isnull=True).exclude(area_en='').order_by('-avg_value')[:10]
        
        return Response({
            'value_trends': list(value_trends),
            'top_valued_areas': list(area_stats)
        })
        
    except Exception as e:
        logger.error(f"Error getting valuation statistics: {e}")
        return Response(
            {'error': 'Failed to get valuation statistics'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )


@api_view(['GET'])
@permission_classes([AllowAny])
def rent_statistics(request):
    """Get rent statistics for charts."""
    try:
        # Get rental property types
        property_stats = Rent.objects.values('property_type').annotate(
            count=Count('id')
        ).order_by('-count')
        
        # Get average rental prices by area
        area_stats = Rent.objects.values('area_en').annotate(
            avg_rent=Avg('contract_amount')
        ).exclude(area_en__isnull=True).exclude(area_en='').order_by('-avg_rent')[:10]
        
        return Response({
            'property_types': list(property_stats),
            'area_prices': list(area_stats)
        })
        
    except Exception as e:
        logger.error(f"Error getting rent statistics: {e}")
        return Response(
            {'error': 'Failed to get rent statistics'},
            status=status.HTTP_500_INTERNAL_SERVER_ERROR
        )

