import mimetypes

from django.http import HttpResponse
from django.shortcuts import get_object_or_404

from management.models.flussi import FlussoAttachment
from management.models.gruppo_driver import GruppoDriver
from management.models.pratica import Pratica
from management.repositories.flusso import FlussoRepository
from management.repositories.gruppo_driver import GruppoDriverRepository
from management.serializers.flusso_serializer import FlussoAttachmentListSerializer, FlussoAttachmentSerializer, FlussoSerializer, FlussoAttachmentListSerializer
from management.serializers.gruppo_driver_serializer import GruppoDriverSerializer
from gestionestudio_api import settings
from .base import *
from django.core.exceptions import SuspiciousOperation
from django.utils.encoding import smart_str

class FlussoAPIView(GenericViewSet):
    authentication_classes = [TokenAuthentication]
    _repository = FlussoRepository()
    _group_repository = GruppoDriverRepository()
    
    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def get_items(self, request: Request, pk:int) -> Response:
        try:
            gruppi = GruppoDriver.objects.filter(plan_id=pk).prefetch_related('flusso_set')
            serializer = GruppoDriverSerializer(gruppi, many=True)
            return Response(serializer.data)

        except Exception as e:
            handle_exception(e)
    
    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def get_item_by_id(self, request: Request, pk: int):
        try:
            flusso = Flusso.objects.get(id=pk)
            serializer = FlussoSerializer(flusso)
            return Response(serializer.data, status=status.HTTP_200_OK)

        except Pratica.DoesNotExist:
            return Response({'detail': 'Plan archivio non trovato.'}, status=status.HTTP_404_NOT_FOUND)

        except Exception as e:
            print(e)
            return Response({'detail': 'An error occurred.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def get_flusso_attachments(self, request, pk=None):
        attachments = FlussoAttachment.objects.filter(plan_id=pk)
        serializer = FlussoAttachmentListSerializer(attachments, many=True)
        
        return Response(serializer.data)

    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def save_group(self, request: Request):
        try:
            dto = request.data
            status_result, group = self._group_repository.save(dto, request.user_id)
            
            if not status_result:
                return Response({'detail': 'An error occurred.'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)

            return Response(group)
        except Exception as e:
            handle_exception(e)
    
    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def change_state(self, request: Request, flusso_id: int):
        try:
            flusso = Flusso.objects.get(id=flusso_id)
            flusso.stato = request.data.get('stato')
            flusso.save()
            
            return Response(GenericResponseObjectSerializer(GenericResponseObject(status=True)).data)
        except Exception as e:
            handle_exception(e)

    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def save_flow(self, request: Request):
        try:
            with transaction.atomic():
                dto = request.data.dict() if hasattr(request.data, 'dict') else dict(request.data)
                success, errors, flow = self._repository.save(dto, request.user_id)
                
                if not success:
                    raise GenericError({"code": "bad_request", "description": errors}, 400)
        
                response_data = {
                    "status": True,
                    "message": "Flusso salvato con successo",
                    "device_request_id": flow.id,
                }

                files = request.FILES.getlist('files')
                if files:
                    upload_response = self.upload_flow_attachments(request, flow.id)
                    
                    if upload_response.status_code >= 400:
                         raise GenericError(upload_response.data, upload_response.status_code)

                    response_data["attachments"] = upload_response.data.get("attachments", [])
                    response_data["attachments_message"] = upload_response.data.get("message")
                
                return Response(
                    response_data,
                    status=status.HTTP_201_CREATED if "id" not in dto else status.HTTP_200_OK
                )
        except Exception as e:
            handle_exception(e)

    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def delete_item(self, request: Request, pk):
        try:
            self._repository.delete(pk, request.user_id)
            return Response(GenericResponseObjectSerializer(GenericResponseObject(status=True)).data)
        except Exception as e:
            handle_exception(e)
    
    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def change_flows_order(self, request: Request):
        try:
            data = request.data 
            if not isinstance(data["payload"], list):
                return Response({"error": "Dati non validi, attesa una lista"}, status=status.HTTP_400_BAD_REQUEST)            
            with transaction.atomic():
                for item in data["payload"]:
                    flow_id = item.get('id')
                    new_order = item.get('codice_attivita')
                    new_group_id = item.get('group')

                    Flusso.objects.filter(id=flow_id).update(
                        codice_attivita=new_order,
                        group_id=new_group_id,
                        data_ora_modifica=timezone.now()
                    )
                return Response(
                    {"message": "Ordinamento aggiornato con successo"}, 
                    status=status.HTTP_200_OK
                )
        except Exception as e:
            handle_exception(e)


    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def upload_flow_attachments(self, request: Request, flusso_id=None):
        flusso_id = flusso_id or request.data.get('flusso_id')
        files = request.FILES.getlist('files')

        if not flusso_id:
            return Response({'error': 'flusso_id è richiesto'}, status=status.HTTP_400_BAD_REQUEST)

        attachments = []
        errors = []
        try:
            with transaction.atomic():
                for file in files:
                    data = {
                        'flusso': flusso_id,
                        'file': file,
                        'nome': file.name,
                    }

                    serializer = FlussoAttachmentSerializer(data=data)
                    if serializer.is_valid():
                        attachment = serializer.save()
                        attachments.append(attachment)
                    else:
                        errors.append({
                            'file': file.name,
                            'errors': serializer.errors
                        })
                if errors:
                    raise Exception("Errori durante il salvataggio")

        except Exception:
            return Response(
                {
                    'error': 'Errore durante il salvataggio dei file',
                    'details': errors
                },
                status=status.HTTP_400_BAD_REQUEST
            )

        result_serializer = FlussoAttachmentListSerializer(attachments, many=True)

        return Response(
            {
                'message': f'{len(attachments)} file salvati con successo',
                'attachments': result_serializer.data
            },
            status=status.HTTP_201_CREATED
        )

    @requires_auth(permission=[os.getenv('GESTORE'), os.getenv('INCARICATO')])
    def download_flusso_attachment(self, request, flusso_id=None, attachment_id=None):        
        try:
            attachment = get_object_or_404(
                FlussoAttachment, 
                id=attachment_id, 
                flusso=flusso_id
            )
            
            relative_path = attachment.location
            file_path = os.path.join(settings.MEDIA_ROOT, relative_path)
            if not os.path.exists(file_path):
                return Response(
                    {'error': 'Attachment non trovato'},
                    status=status.HTTP_404_NOT_FOUND
                )
            
            # Verifica di sicurezza: prevenzione path traversal
            file_path = os.path.abspath(file_path)
            if not self._is_safe_path(file_path):
                raise SuspiciousOperation("Percorso file non sicuro")
            
            # Determina il tipo MIME
            content_type, _ = mimetypes.guess_type(file_path)
            if content_type is None:
                content_type = 'application/octet-stream'
            
            # Legge il file
            try:
                with open(file_path, 'rb') as file:
                    file_content = file.read()
            except IOError:
                return Response(
                    {'error': 'Impossibile leggere il file'},
                    status=status.HTTP_404_NOT_FOUND
                )
            
            response = HttpResponse(file_content, content_type=content_type)
            filename = self._get_safe_filename(attachment.nome or os.path.basename(file_path))
            response['Content-Disposition'] = f'attachment; filename="{smart_str(filename)}"'
            response['Content-Length'] = len(file_content)
            
            # Headers di sicurezza aggiuntivi
            response['X-Content-Type-Options'] = 'nosniff'
            response['X-Frame-Options'] = 'DENY'
            
            return response
            
        except FlussoAttachment.DoesNotExist:
                return Response(
                    {'error': 'Attachment non trovato'},
                    status=status.HTTP_404_NOT_FOUND
                )
        except Exception as e:
            import logging
            logger = logging.getLogger(__name__)
            logger.error(f"Errore nel download dell'allegato {attachment_id}: {str(e)}")
            return Response(
                {'error': 'Errore lettura file'},
                status=status.HTTP_404_NOT_FOUND
            )

    def _is_safe_path(self, file_path):
        SAFE_ROOT = os.path.abspath(os.path.join(settings.MEDIA_ROOT, 'flusso_allegati'))
        file_path = os.path.normpath(file_path)
        
        return file_path.startswith(SAFE_ROOT)
    
    def _get_safe_filename(self, filename):
        import re
        safe_filename = re.sub(r'[^\w\s.-]', '', filename).strip()
        
        if len(safe_filename) > 255:
            name, ext = os.path.splitext(safe_filename)
            safe_filename = name[:250] + ext
        
        return safe_filename or 'attachment'
        