from rest_framework import viewsets, status
from rest_framework.decorators import action
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated, IsAdminUser, BasePermission
from django.shortcuts import get_object_or_404
from django.utils.timezone import now
import logging

from organization.models import Organization, SyncLog, SyncConfiguration
from organization.serializers import (
    OrganizationSerializer,
    SyncLogSerializer,
    SyncConfigurationSerializer,
    SyncOrganizationRequestSerializer,
    SyncOrganizationResponseSerializer,
    TestConnectionSerializer,
)
from organization.services.sync_service import (
    OrganizationSyncService,
    SyncError,
)
from permissions import (
    DynamicModelPermission,
    user_has_organization_membership,
    resolve_effective_permission_strings,
    group_permissions_by_model,
)

logger = logging.getLogger(__name__)


class IsSuperUser(BasePermission):
    """Allow access only to superusers."""

    def has_permission(self, request, view):
        return bool(request.user and request.user.is_authenticated and request.user.is_superuser)


class OrganizationViewSet(viewsets.ModelViewSet):
    """
    API ViewSet for managing organizations.
    
    Endpoints:
    - GET /api/organizations/ - List organizations (Superuser only)
    - POST /api/organizations/ - Create organization (Superuser only)
    - GET /api/organizations/{id}/ - Retrieve organization (Superuser only)
    - PUT/PATCH /api/organizations/{id}/ - Update organization (Superuser only)
    - DELETE /api/organizations/{id}/ - Delete organization (Superuser only)
    - GET /api/organizations/assigned/ - List organizations assigned to current user
    - GET /api/organizations/{id}/assigned_detail/ - Retrieve specific assigned organization
    """

    queryset = Organization.objects.all()
    serializer_class = OrganizationSerializer
    permission_classes = [IsAuthenticated, IsSuperUser]
    filterset_fields = ["status", "is_active", "external_source"]
    search_fields = ["name", "code", "email", "external_id"]
    ordering_fields = ["created_at", "updated_at", "name"]
    ordering = ["-created_at"]

    @action(detail=False, methods=["get"], permission_classes=[IsAuthenticated])
    def assigned(self, request):
        """
        Get organizations assigned to the current user via Membership.
        """
        user = request.user
        if user.is_superuser:
            organizations = Organization.objects.all()
        else:
            organizations = Organization.objects.filter(memberships__user=user).distinct()
            
        serializer = self.get_serializer(organizations, many=True)
        return Response(serializer.data)

    @action(detail=True, methods=["get"], url_path="assigned", permission_classes=[IsAuthenticated])
    def assigned_detail(self, request, pk=None):
        """
        Get details of a specific organization if assigned to the current user.
        URL: /api/organizations/{id}/assigned/
        """
        user = request.user
        organization = get_object_or_404(Organization, pk=pk)
        
        if not user.is_superuser:
            # Check if user has membership in this organization
            if not Organization.objects.filter(pk=pk, memberships__user=user).exists():
                return Response(
                    {"error": "You do not have access to this organization."},
                    status=status.HTTP_403_FORBIDDEN
                )
                
        serializer = self.get_serializer(organization)
        return Response(serializer.data)

    @action(detail=False, methods=["post"], permission_classes=[IsAuthenticated, IsSuperUser])
    def sync(self, request):
        """
        Manually trigger organization sync from external API.
        
        POST /api/organizations/sync/
        
        Request body:
        {
            "source_name": "default_source",  # optional
            "max_records": null,              # optional
            "triggered_by": "admin@example.com"  # optional
        }
        
        Response:
        {
            "sync_id": 1,
            "status": "completed",
            "message": "Sync completed successfully",
            "external_source": "default_source",
            "started_at": "2024-01-15T10:30:00Z",
            "completed_at": "2024-01-15T10:35:00Z",
            "total_fetched": 10,
            "created_count": 3,
            "updated_count": 5,
            "error_count": 2,
            "duration_seconds": 300.5,
            "error_message": ""
        }
        """
        # Validate request
        serializer = SyncOrganizationRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)

        try:
            # Get sync configuration
            source_name = serializer.validated_data.get(
                "source_name",
                SyncConfiguration.objects.filter(is_active=True).first().source_name
            )
            config = get_object_or_404(
                SyncConfiguration,
                source_name=source_name,
                is_active=True
            )

            max_records = serializer.validated_data.get("max_records")
            triggered_by = serializer.validated_data.get(
                "triggered_by",
                getattr(request.user, "email", request.user.username)
            )

            # Create sync log entry
            sync_log = SyncLog.objects.create(
                external_source=config.source_name,
                triggered_by=triggered_by,
                sync_type="partial" if max_records else "full"
            )

            logger.info(
                f"Starting sync (ID: {sync_log.sync_id}) "
                f"triggered by {triggered_by}"
            )

            # Perform sync
            sync_service = OrganizationSyncService(config)
            sync_service.sync_organizations(sync_log, max_records=max_records)

            # Prepare response
            response_data = {
                "sync_id": sync_log.sync_id,
                "status": sync_log.status,
                "message": self._get_status_message(sync_log),
                "external_source": sync_log.external_source,
                "started_at": sync_log.started_at,
                "completed_at": sync_log.completed_at,
                "total_fetched": sync_log.total_fetched,
                "created_count": sync_log.created_count,
                "updated_count": sync_log.updated_count,
                "error_count": sync_log.error_count,
                "duration_seconds": sync_log.duration_seconds,
                "error_message": sync_log.error_message or ""
            }

            status_code = status.HTTP_200_OK if sync_log.is_successful else status.HTTP_207_MULTI_STATUS

            return Response(response_data, status=status_code)

        except SyncError as e:
            logger.error(f"Sync failed: {str(e)}")
            return Response(
                {
                    "error": str(e),
                    "detail": "Organization sync failed. Please check logs for details."
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

        except Exception as e:
            logger.error(f"Unexpected error during sync: {str(e)}", exc_info=True)
            return Response(
                {
                    "error": "An unexpected error occurred",
                    "detail": str(e)
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"], permission_classes=[IsAuthenticated, IsSuperUser])
    def sync_history(self, request):
        """
        Get sync operation history.
        
        GET /api/organizations/sync_history/?limit=10&offset=0
        """
        limit = request.query_params.get("limit", 10)
        offset = request.query_params.get("offset", 0)

        try:
            limit = int(limit)
            offset = int(offset)
        except (ValueError, TypeError):
            limit = 10
            offset = 0

        sync_logs = SyncLog.objects.all()[offset:offset + limit]
        serializer = SyncLogSerializer(sync_logs, many=True)

        return Response({
            "count": SyncLog.objects.count(),
            "offset": offset,
            "limit": limit,
            "results": serializer.data
        })

    @action(detail=False, methods=["get"], permission_classes=[IsAuthenticated, IsSuperUser])
    def sync_stats(self, request):
        """
        Get sync statistics and summary.
        
        GET /api/organizations/sync_stats/
        """
        from django.db.models import Sum, Count, Q
        from datetime import timedelta
        from django.utils import timezone

        # Calculate statistics
        total_syncs = SyncLog.objects.count()
        successful_syncs = SyncLog.objects.filter(status=SyncLog.SyncStatus.COMPLETED).count()
        failed_syncs = SyncLog.objects.filter(status=SyncLog.SyncStatus.FAILED).count()
        partial_syncs = SyncLog.objects.filter(status=SyncLog.SyncStatus.PARTIAL).count()

        # Get last 24 hours stats
        last_24h = timezone.now() - timedelta(hours=24)
        last_24h_stats = SyncLog.objects.filter(
            started_at__gte=last_24h
        ).aggregate(
            syncs=Count("sync_id"),
            total_created=Sum("created_count"),
            total_updated=Sum("updated_count"),
            total_errors=Sum("error_count")
        )

        return Response({
            "total_syncs": total_syncs,
            "successful_syncs": successful_syncs,
            "failed_syncs": failed_syncs,
            "partial_syncs": partial_syncs,
            "success_rate": (successful_syncs / total_syncs * 100) if total_syncs > 0 else 0,
            "last_24h": {
                "syncs": last_24h_stats["syncs"] or 0,
                "created": last_24h_stats["total_created"] or 0,
                "updated": last_24h_stats["total_updated"] or 0,
                "errors": last_24h_stats["total_errors"] or 0
            }
        })

    def _get_status_message(self, sync_log: SyncLog) -> str:
        """Generate user-friendly status message."""
        if sync_log.status == SyncLog.SyncStatus.COMPLETED:
            return (
                f"Sync completed successfully. "
                f"Created: {sync_log.created_count}, "
                f"Updated: {sync_log.updated_count}"
            )
        elif sync_log.status == SyncLog.SyncStatus.PARTIAL:
            return (
                f"Sync completed with errors. "
                f"Created: {sync_log.created_count}, "
                f"Updated: {sync_log.updated_count}, "
                f"Errors: {sync_log.error_count}"
            )
        elif sync_log.status == SyncLog.SyncStatus.FAILED:
            return f"Sync failed: {sync_log.error_message}"
        else:
            return "Sync in progress"


class SyncConfigurationViewSet(viewsets.ModelViewSet):
    """
    API ViewSet for managing sync configurations.
    Only super admins can view/modify configurations.
    """

    queryset = SyncConfiguration.objects.all()
    serializer_class = SyncConfigurationSerializer
    permission_classes = [IsAuthenticated, IsAdminUser]
    lookup_field = "source_name"
    filterset_fields = ["is_active"]
    ordering = ["source_name"]

    @action(detail=True, methods=["post"])
    def test_connection(self, request, source_name=None):
        """
        Test connection to external API for a specific configuration.
        
        POST /api/sync-config/{source_name}/test_connection/
        """
        config = self.get_object()

        try:
            success, message = config.test_connection()
            
            return Response({
                "success": success,
                "message": message,
                "source_name": config.source_name,
                "last_tested_at": config.last_tested_at
            }, status=status.HTTP_200_OK if success else status.HTTP_400_BAD_REQUEST)

        except Exception as e:
            logger.error(f"Connection test failed for {source_name}: {str(e)}")
            return Response(
                {
                    "success": False,
                    "error": str(e),
                    "source_name": config.source_name
                },
                status=status.HTTP_500_INTERNAL_SERVER_ERROR
            )

    @action(detail=False, methods=["get"])
    def active(self, request):
        """
        Get the currently active sync configuration.
        
        GET /api/sync-config/active/
        """
        config = SyncConfiguration.objects.filter(is_active=True).first()
        
        if not config:
            return Response(
                {"detail": "No active sync configuration found"},
                status=status.HTTP_404_NOT_FOUND
            )

        serializer = self.get_serializer(config)
        return Response(serializer.data)


class SyncLogViewSet(viewsets.ReadOnlyModelViewSet):
    """
    Read-only ViewSet for sync logs.
    Provides audit trail of all sync operations.
    """

    queryset = SyncLog.objects.all()
    serializer_class = SyncLogSerializer
    permission_classes = [IsAuthenticated, IsAdminUser]
    filterset_fields = ["external_source", "status", "sync_type"]
    search_fields = ["external_source", "triggered_by"]
    ordering_fields = ["started_at", "completed_at", "status"]
    ordering = ["-started_at"]


# ─── Dynamic Permission Endpoint ─────────────────────────────────────────────

from rest_framework.decorators import api_view, permission_classes as drf_permission_classes
from django.contrib.auth.models import Permission


@api_view(['GET'])
@drf_permission_classes([IsAuthenticated])
def my_permissions_view(request):
    """
    GET /api/my-permissions/

    Returns the current user's effective permissions scoped to the active
    organization, in a frontend-friendly format:

    {
        "teacher": ["view", "add"],
        "timeslot": ["view"]
    }

    Superusers receive {"*": ["*"]} — full access signal.
    With an organization: same whitelist as auth/permissions/ (only models with
    OrganizationModelAccess is_enabled=True for that org).
    """
    user = request.user

    if (
        getattr(request, 'organization_header_provided', False)
        and not getattr(request, 'organization_header_valid', True)
    ):
        return Response({"detail": "Invalid X-Organization header"}, status=status.HTTP_400_BAD_REQUEST)

    if user.is_superuser:
        return Response({"*": ["*"]})

    organization = getattr(request, 'organization', None)
    org_header = getattr(request, 'organization_header_provided', False)

    if organization is not None:
        if not user_has_organization_membership(
            user,
            organization,
            organization_header_provided=org_header,
        ):
            return Response(
                {"detail": "You are not assigned to this organization"},
                status=status.HTTP_403_FORBIDDEN,
            )
        perm_strings = resolve_effective_permission_strings(
            user,
            organization=organization,
            strict_enabled_only=True,
            organization_header_provided=org_header,
        )
        return Response(group_permissions_by_model(perm_strings))

    # No org context: direct user permissions only (unchanged behavior)
    action_map = {'view': 'view', 'add': 'add', 'change': 'change', 'delete': 'delete'}
    result = {}
    all_perms = (
        Permission.objects
        .filter(user=user)
        .select_related('content_type')
        .values_list('content_type__model', 'codename')
    )
    for model_name, codename in all_perms:
        action = codename.split('_')[0] if '_' in codename else codename
        if action in action_map:
            result.setdefault(model_name, [])
            if action not in result[model_name]:
                result[model_name].append(action)

    return Response(result)
