from rest_framework import viewsets, status, filters
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticatedOrReadOnly, IsAuthenticated
from django_filters.rest_framework import DjangoFilterBackend
from django.db.models import Q
from django.utils.decorators import method_decorator
from django.views.decorators.cache import cache_page
from .models import Governorate, Category, Amenity, Place
from .utils import calculate_distance
from .serializers import (
    GovernorateSerializer,
    CategorySerializer,
    AmenitySerializer,
    PlaceListSerializer,
    PlaceDetailSerializer,
    PlaceCreateUpdateSerializer
)
from common.permissions import IsPlaceAdmin, IsPlaceOwner
from common.pagination import StandardResultsSetPagination


class GovernorateViewSet(viewsets.ReadOnlyModelViewSet):
    """Governorate ViewSet"""
    queryset = Governorate.objects.all()
    serializer_class = GovernorateSerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    
    @method_decorator(cache_page(60 * 60))  # Cache for 1 hour
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class CategoryViewSet(viewsets.ReadOnlyModelViewSet):
    """Category ViewSet"""
    queryset = Category.objects.filter(is_active=True)
    serializer_class = CategorySerializer
    permission_classes = [IsAuthenticatedOrReadOnly]
    
    @method_decorator(cache_page(60 * 60))  # Cache for 1 hour
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)


class AmenityViewSet(viewsets.ReadOnlyModelViewSet):
    """Amenity ViewSet"""
    queryset = Amenity.objects.all()
    serializer_class = AmenitySerializer
    permission_classes = [IsAuthenticatedOrReadOnly]


class PlaceViewSet(viewsets.ModelViewSet):
    """Place ViewSet"""
    queryset = Place.objects.filter(is_active=True)
    permission_classes = [IsAuthenticatedOrReadOnly]
    pagination_class = StandardResultsSetPagination
    filter_backends = [DjangoFilterBackend, filters.SearchFilter, filters.OrderingFilter]
    search_fields = ['name', 'description', 'address']
    ordering_fields = ['rating', 'reviews_count', 'created_at']
    ordering = ['-rating', '-reviews_count']
    
    def get_serializer_class(self):
        if self.action == 'list':
            return PlaceListSerializer
        elif self.action in ['create', 'update', 'partial_update']:
            return PlaceCreateUpdateSerializer
        return PlaceDetailSerializer
    
    def get_queryset(self):
        queryset = Place.objects.filter(is_active=True).select_related(
            'category', 'governorate', 'owner'
        ).prefetch_related('amenities')
        
        # Filter by category
        category = self.request.query_params.get('category')
        if category:
            queryset = queryset.filter(category__slug=category)
        
        # Filter by governorate
        governorate = self.request.query_params.get('governorate')
        if governorate:
            queryset = queryset.filter(governorate__code=governorate)
        
        # Filter by is_open
        is_open = self.request.query_params.get('is_open')
        if is_open is not None:
            queryset = queryset.filter(is_open=is_open.lower() == 'true')
        
        # Filter by has_booking
        has_booking = self.request.query_params.get('has_booking')
        if has_booking is not None:
            queryset = queryset.filter(has_booking=has_booking.lower() == 'true')
        
        # Filter by min_rating
        min_rating = self.request.query_params.get('min_rating')
        if min_rating:
            try:
                queryset = queryset.filter(rating__gte=float(min_rating))
            except ValueError:
                pass
        
        # Filter by max_distance (requires user location)
        max_distance = self.request.query_params.get('max_distance')
        user_lat = self.request.query_params.get('lat')
        user_lng = self.request.query_params.get('lng')
        
        if max_distance and user_lat and user_lng:
            try:
                max_distance = float(max_distance)
                user_lat = float(user_lat)
                user_lng = float(user_lng)
                
                # Filter places within max_distance
                filtered_places = []
                for place in queryset:
                    distance = calculate_distance(
                        user_lat, user_lng,
                        float(place.latitude), float(place.longitude)
                    )
                    if distance <= max_distance:
                        filtered_places.append(place.id)
                
                queryset = queryset.filter(id__in=filtered_places)
            except (ValueError, TypeError):
                pass
        
        return queryset
    
    @action(detail=False, methods=['get'])
    def nearby(self, request):
        """Get nearby places"""
        lat = request.query_params.get('lat')
        lng = request.query_params.get('lng')
        max_distance = float(request.query_params.get('max_distance', 10))
        
        if not lat or not lng:
            return Response(
                {'error': 'lat and lng parameters are required'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        try:
            lat = float(lat)
            lng = float(lng)
        except (ValueError, TypeError):
            return Response(
                {'error': 'Invalid lat or lng values'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Get all places and calculate distances
        places = self.get_queryset()
        nearby_places = []
        
        for place in places:
            distance = calculate_distance(
                lat, lng,
                float(place.latitude), float(place.longitude)
            )
            if distance <= max_distance:
                place_data = PlaceListSerializer(place, context={'request': request}).data
                place_data['distance_km'] = round(distance, 2)
                nearby_places.append(place_data)
        
        # Sort by distance
        nearby_places.sort(key=lambda x: x['distance_km'])
        
        return Response(nearby_places)
    
    @action(detail=False, methods=['get'], url_path='search-suggestions')
    def search_suggestions(self, request):
        """Get search suggestions based on query"""
        query = request.query_params.get('q', '').strip()
        limit = int(request.query_params.get('limit', 10))
        
        if not query or len(query) < 2:
            return Response([])
        
        # Get suggestions from place names, categories, and governorates
        suggestions = []
        
        # Place names
        places = Place.objects.filter(
            is_active=True,
            name__icontains=query
        ).values_list('name', flat=True).distinct()[:limit]
        
        for place_name in places:
            suggestions.append({
                'type': 'place',
                'text': place_name,
                'icon': 'fa-map-marker-alt'
            })
        
        # Categories
        categories = Category.objects.filter(
            is_active=True,
            name_ar__icontains=query
        ).values_list('name_ar', flat=True).distinct()[:5]
        
        for cat_name in categories:
            suggestions.append({
                'type': 'category',
                'text': cat_name,
                'icon': 'fa-tag'
            })
        
        # Governorates
        governorates = Governorate.objects.filter(
            name_ar__icontains=query
        ).values_list('name_ar', flat=True).distinct()[:5]
        
        for gov_name in governorates:
            suggestions.append({
                'type': 'governorate',
                'text': gov_name,
                'icon': 'fa-city'
            })
        
        return Response(suggestions[:limit])
    
    @action(detail=False, methods=['get'], url_path='my-place')
    def my_place(self, request):
        """Get place for place admin"""
        user = request.user
        
        if user.role != 'place_admin' or not hasattr(user, 'place_admin_profile'):
            return Response(
                {'error': 'You are not a place admin'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        place = user.place_admin_profile.place
        serializer = PlaceDetailSerializer(place, context={'request': request})
        return Response(serializer.data)
    
    @action(detail=True, methods=['post'])
    def gallery(self, request, pk=None):
        """Add image to place gallery"""
        place = self.get_object()
        
        # Check permissions
        if not self._can_manage_place(request.user, place):
            return Response(
                {'error': 'You do not have permission to manage this place'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        if 'image' not in request.FILES:
            return Response(
                {'error': 'No image file provided'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        from common.utils import save_image
        from django.conf import settings
        
        try:
            image_file = request.FILES['image']
            image_path = f'places/{place.id}/gallery/{image_file.name}'
            saved_path = save_image(image_file, image_path, optimize=True)
            
            # Add to gallery
            gallery = list(place.gallery) if place.gallery else []
            gallery.append(settings.MEDIA_URL + saved_path)
            place.gallery = gallery
            place.save(update_fields=['gallery'])
            
            return Response({
                'message': 'Image added to gallery',
                'gallery': place.gallery
            })
        except Exception as e:
            return Response(
                {'error': str(e)},
                status=status.HTTP_400_BAD_REQUEST
            )
    
    @action(detail=True, methods=['delete'], url_path='gallery/(?P<image_index>[0-9]+)')
    def delete_gallery_image(self, request, pk=None, image_index=None):
        """Delete image from place gallery"""
        place = self.get_object()
        
        # Check permissions
        if not self._can_manage_place(request.user, place):
            return Response(
                {'error': 'You do not have permission to manage this place'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        try:
            image_index = int(image_index)
            gallery = list(place.gallery) if place.gallery else []
            
            if image_index < 0 or image_index >= len(gallery):
                return Response(
                    {'error': 'Invalid image index'},
                    status=status.HTTP_400_BAD_REQUEST
                )
            
            # Remove image
            gallery.pop(image_index)
            place.gallery = gallery
            place.save(update_fields=['gallery'])
            
            return Response({
                'message': 'Image removed from gallery',
                'gallery': place.gallery
            })
        except ValueError:
            return Response(
                {'error': 'Invalid image index'},
                status=status.HTTP_400_BAD_REQUEST
            )
    
    @action(detail=True, methods=['patch'], url_path='gallery/reorder')
    def reorder_gallery(self, request, pk=None):
        """Reorder gallery images"""
        place = self.get_object()
        
        # Check permissions
        if not self._can_manage_place(request.user, place):
            return Response(
                {'error': 'You do not have permission to manage this place'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        new_order = request.data.get('order', [])
        if not isinstance(new_order, list):
            return Response(
                {'error': 'Order must be a list'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        gallery = list(place.gallery) if place.gallery else []
        if len(new_order) != len(gallery):
            return Response(
                {'error': 'Order length does not match gallery length'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        # Reorder gallery
        reordered_gallery = [gallery[i] for i in new_order if 0 <= i < len(gallery)]
        place.gallery = reordered_gallery
        place.save(update_fields=['gallery'])
        
        return Response({
            'message': 'Gallery reordered',
            'gallery': place.gallery
        })
    
    @action(detail=True, methods=['patch'], url_path='working-hours')
    def update_working_hours(self, request, pk=None):
        """Update place working hours"""
        place = self.get_object()
        
        # Check permissions
        if not self._can_manage_place(request.user, place):
            return Response(
                {'error': 'You do not have permission to manage this place'},
                status=status.HTTP_403_FORBIDDEN
            )
        
        working_hours = request.data.get('working_hours')
        if not isinstance(working_hours, dict):
            return Response(
                {'error': 'working_hours must be a dictionary'},
                status=status.HTTP_400_BAD_REQUEST
            )
        
        place.working_hours = working_hours
        place.save(update_fields=['working_hours'])
        
        return Response({
            'message': 'Working hours updated',
            'working_hours': place.working_hours
        })
    
    def _can_manage_place(self, user, place):
        """Check if user can manage place"""
        if user.role in ['super_admin', 'admin']:
            return True
        if user.role == 'place_admin' and hasattr(user, 'place_admin_profile'):
            return user.place_admin_profile.place == place
        return False
    
    @action(detail=True, methods=['post', 'delete'])
    def favorite(self, request, pk=None):
        """Add or remove place from favorites"""
        place = self.get_object()
        from notifications.models import Favorite
        
        if request.method == 'POST':
            favorite, created = Favorite.objects.get_or_create(
                user=request.user,
                place=place
            )
            if created:
                return Response({'message': 'Added to favorites'}, status=status.HTTP_201_CREATED)
            return Response({'message': 'Already in favorites'}, status=status.HTTP_200_OK)
        
        elif request.method == 'DELETE':
            Favorite.objects.filter(user=request.user, place=place).delete()
            return Response({'message': 'Removed from favorites'}, status=status.HTTP_200_OK)
