from django.shortcuts import render, get_object_or_404, redirect
from django.contrib.auth.decorators import login_required, user_passes_test
from django.contrib import messages
from django.contrib.admin.views.decorators import staff_member_required

from django.core.mail import EmailMultiAlternatives
from django.conf import settings
from django.template.loader import render_to_string
from django.utils import timezone
from datetime import date

import logging

from django.core.mail import send_mass_mail
from django.core.mail import BadHeaderError
from smtplib import SMTPException

from django.http import HttpResponse

from rest_framework.response import Response
from rest_framework.decorators import api_view
from .serializers import EventoSerializer, ReservaSerializer, ListaEsperaSerializer, NoticiaSerializer

from .models import Evento, Reserva, ListaEspera, Noticia
from .forms import ListaEsperaForm, EventoForm, MensajeCorreoForm

from django.contrib.auth import get_user_model
from django.db.models import Count

User = get_user_model()

logger = logging.getLogger(__name__)


@login_required
def reservar_evento(request, evento_id):
    evento = get_object_or_404(Evento, id=evento_id)

    # Verificar si el usuario ya tiene una reserva para este evento
    if Reserva.objects.filter(evento=evento, usuario=request.user).exists():
        messages.error(request, 'Ya tienes una reserva para este evento.')
        return redirect('eventos:lista_eventos')

    # Verificar si quedan plazas disponibles
    if evento.plazas_restantes() > 0:
        Reserva.objects.create(evento=evento, usuario=request.user)

        # Enviar un correo con la información del evento
        asunto = f'Confirmación de Reserva para {evento.nombre}'
        from_email = settings.DEFAULT_FROM_EMAIL
        to_email = request.user.email
        cc_email = [settings.ADMIN_EMAIL]

        texto = f'Hola {request.user.nombre}, \n\n' \
                f'Te has inscrito correctamente en el evento "{evento.nombre}".\n' \
                f'Detalles del evento:\n' \
                f'Nombre: {evento.nombre}\n' \
                f'Descripción: {evento.descripcion}\n' \
                f'Fecha: {evento.fecha.strftime("%d/%b/%y")}  a las {evento.hora}\n' \
                f'\n¡Gracias por inscribirte!\n\n'

        html_content = render_to_string('emails/confirmacion_reserva.html', {'evento': evento, 'usuario': request.user})

        # Enviar el correo con el administrador en copia
        msg = EmailMultiAlternatives(asunto, texto, from_email, [to_email], cc=cc_email)
        msg.attach_alternative(html_content, "text/html")
        msg.send()

        # Aquí hay que mirar si quedan más de n plazas.
        # n es un numero aleatorio entre 2 y el total de plazas
        # si es así, y ya hay añadir a dos participantes.

        messages.success(request,
                         f'Reserva realizada para el evento {evento.nombre}.')

    else:
        messages.error(request, 'Lo sentimos, no quedan plazas disponibles.')

    return redirect('eventos:lista_eventos')


@user_passes_test(lambda u: u.is_staff)
def publicar_evento(request, evento_id):
    evento = get_object_or_404(Evento, id=evento_id)

    if not evento.publicado:
        evento.publicado = True
        evento.save()
        messages.success(request, 'El evento %s ha sido publicado' % evento.nombre)
    else:
        messages.info(request, 'El evento %s ya está publicado' % evento.nombre)

    return redirect('eventos:detalle_evento', evento_id=evento_id)


@login_required
def lista_eventos(request):
    # anno_actual = timezone.now().year

    # Solo mostrar los eventos publicados a los usuarios normales
    if request.user.is_staff:
        eventos = Evento.objects.all().order_by('-fecha', '-hora')
    else:
        eventos = Evento.objects.filter(publicado=True).order_by('-fecha', '-hora')
#        eventos = Evento.objects.filter(publicado=True, fecha__year=anno_actual)  # Los usuarios ven solo eventos publicados y del año en curso

    # Crear un diccionario que mapea cada evento con un booleano indicando si el usuario ya ha reservado
    eventos_con_reserva = []
    for evento in eventos:
        inscrito = Reserva.objects.filter(evento=evento, usuario=request.user).exists() if request.user.is_authenticated else False

        if ListaEspera.objects.filter(evento=evento, usuario=request.user).exists():
            en_espera = True
        else:
            en_espera = False

        eventos_con_reserva.append({
            'evento': evento,
            'inscrito': inscrito,
            'id': evento.id,
            'nombre': evento.nombre,
            'fecha': evento.fecha,
            'hora': evento.hora,
            'plazas_disponibles': evento.plazas_disponibles,
            'plazas_restantes': evento.plazas_restantes,
            'en_espera': en_espera,
            'usuario': request.user,
            'publicado': evento.publicado,
            'url_imagen': evento.url_imagen
        })

    return render(request, 'eventos/lista_eventos.html', {'eventos_con_reserva': eventos_con_reserva})


def detalle_noticia(request, noticia_id):
    noticia = get_object_or_404(Noticia, id=noticia_id, publicado=True)
    return render(request, 'detalle_noticia.html', {'noticia': noticia})


def ayuda(request):

    return render(request, 'eventos/help.html')


@login_required
def detalle_evento(request, evento_id):
    el_evento = []

    evento = get_object_or_404(Evento, pk=evento_id)

    reservas = Reserva.objects.filter(evento=evento_id)

    inscrito = Reserva.objects.filter(evento=evento, usuario=request.user).exists() if request.user.is_authenticated else False

    if ListaEspera.objects.filter(evento=evento, usuario=request.user).exists():
        en_espera = True
    else:
        en_espera = False

    lista_espera = ListaEspera.objects.filter(evento=evento_id)

    el_evento.append(
        {
            'evento': evento,
            'inscrito': inscrito,
            'id': evento.id,
            'nombre': evento.nombre,
            'descripcion': evento.descripcion,
            'fecha': evento.fecha,
            'plazas_disponibles': evento.plazas_disponibles,
            'plazas_restantes': evento.plazas_restantes,
            'en_espera': en_espera,
            'usuario': request.user,
            'publicado': evento.publicado,
            'url_imagen': evento.url_imagen
        }
    )

    return render(request, 'eventos/detalle_evento.html', {'eventos': el_evento, 'reservas': reservas, 'lista': lista_espera})


# Verificar si el usuario es administrador
def es_admin(user):
    return user.is_staff


@user_passes_test(es_admin)
def crear_evento(request):
    if request.method == 'POST':
        form = EventoForm(request.POST)
        if form.is_valid():
            form.save()
            messages.success(request, 'Evento creado exitosamente.')
            return redirect('eventos:lista_eventos')  # Redirige a la lista de eventos
    else:
        form = EventoForm()

    return render(request, 'eventos/crear_evento.html', {'form': form})


@user_passes_test(lambda u: u.is_staff)
def editar_evento(request, evento_id):
    evento = get_object_or_404(Evento, id=evento_id)
    if request.method == 'POST':
        form = EventoForm(request.POST, instance=evento)
        if form.is_valid():
            form.save()
            return redirect('lista_eventos')
    else:
        form = EventoForm(instance=evento)
    return render(request, 'crear_evento.html', {'form': form, 'evento': evento})


@login_required
def apuntar_lista_espera(request, evento_id):
    evento = get_object_or_404(Evento, id=evento_id)

    # Verificar si el usuario ya está en la lista de espera
    if ListaEspera.objects.filter(evento=evento, usuario=request.user).exists():
        messages.error(request, 'Ya estás en la lista de espera para este evento.')
        return redirect('eventos:lista_eventos')

    # Crear una entrada en la lista de espera
    if request.method == 'POST':
        form = ListaEsperaForm(request.POST)
        if form.is_valid():
            ListaEspera.objects.create(evento=evento, usuario=request.user)
            messages.success(request, 'Te has apuntado a la lista de espera para este evento.')
            return redirect('eventos:lista_eventos')

    form = ListaEsperaForm()
    return render(request, 'eventos/apuntar_lista_espera.html', {'form': form, 'evento': evento})


#
# API
#

@api_view(['GET'])
def api_lista_eventos(request):
    """Devuelve la lista de todos los eventos."""
    eventos = Evento.objects.all()
    serializer = EventoSerializer(eventos, many=True)
    return Response(serializer.data)


@api_view(['GET'])
def api_detalle_evento(request, evento_id):
    """Devuelve los detalles de un evento específico."""
    try:
        evento = Evento.objects.get(id=evento_id)
        serializer = EventoSerializer(evento)
        return Response(serializer.data)
    except Evento.DoesNotExist:
        return Response({'error': 'Evento no encontrado'}, status=404)


@api_view(['GET'])
def api_lista_reservas(request):
    """Devuelve la lista de todos los reservas."""
    reservas = Reserva.objects.all()
    serializer = ReservaSerializer(reservas, many=True)
    return Response(serializer.data)


@api_view(['GET'])
def api_detalle_reserva(request, reserva_id):
    """Devuelve los detalles de un reserva específico."""
    try:
        reserva = Reserva.objects.get(id=reserva_id)
        serializer = ReservaSerializer(reserva)
        return Response(serializer.data)
    except Reserva.DoesNotExist:
        return Response({'error': 'Reserva no encontrado'}, status=404)


@api_view(['GET'])
def api_lista_listaespera(request):
    """Devuelve la lista de todos los listaespera."""
    listaesperas = ListaEspera.objects.all()
    serializer = ListaEsperaSerializer(listaesperas, many=True)
    return Response(serializer.data)


@api_view(['GET'])
def api_detalle_listaespera(request, listaespera_id):
    """Devuelve los detalles de un listaespera específico."""
    try:
        listaespera = ListaEspera.objects.get(id=listaespera_id)
        serializer = ListaEsperaSerializer(listaespera)
        return Response(serializer.data)
    except ListaEspera.DoesNotExist:
        return Response({'error': 'ListaEspera no encontrado'}, status=404)


@api_view(['GET'])
def api_lista_noticias(request):
    """Devuelve la lista de todos los noticias."""
    noticias = Noticia.objects.all()
    serializer = NoticiaSerializer(noticias, many=True)
    return Response(serializer.data)


@api_view(['GET'])
def api_detalle_noticia(request, noticia_id):
    """Devuelve los detalles de un noticia específico."""
    try:
        noticia = Noticia.objects.get(id=noticia_id)
        serializer = NoticiaSerializer(noticia)
        return Response(serializer.data)
    except Noticia.DoesNotExist:
        return Response({'error': 'Noticia no encontrado'}, status=404)


# Verifica si el usuario es administrador
def es_admin(user):
    return user.is_staff


@user_passes_test(es_admin)  # Solo administradores pueden acceder
def enviar_correo_inscritos(request, evento_id):
    evento = get_object_or_404(Evento, id=evento_id)
    inscritos = Reserva.objects.filter(evento=evento).select_related('usuario')

    if not inscritos:
        messages.warning(request, "No hay usuarios inscritos en este evento.")
        return redirect('eventos:detalle_evento', evento_id=evento.id)

    destinatarios = [reserva.usuario.email for reserva in inscritos if reserva.usuario.email]

    if not destinatarios:
        messages.warning(request, "No hay correos disponibles para los inscritos.")
        return redirect('eventos:detalle_evento', evento_id=evento.id)

    if request.method == "POST":
        form = MensajeCorreoForm(request.POST)
        if form.is_valid():
            asunto_usuario = form.cleaned_data['asunto']
            mensaje_usuario = form.cleaned_data['mensaje']

            asunto = f"{asunto_usuario} - {evento.nombre}"

            firma = "\n\n\nSaludos, \n\nPádel entre amigos"
            mensaje = f"{mensaje_usuario}{firma}"

            mensajes = [
                (asunto, mensaje, settings.DEFAULT_FROM_EMAIL, [email]) for email in destinatarios
            ]

            try:
                send_mass_mail(mensajes, fail_silently=False)
            except BadHeaderError:
                return HttpResponse('Encabezado inválido encontrado.')
            except SMTPException as e:
                return HttpResponse(f'Error de SMTP: {e}')
            except Exception as e:
                return HttpResponse(f'Ocurrió un error: {e}')

            logger.debug('Correo enviado')

            messages.success(request, f"Correo enviado a {len(destinatarios)} inscritos.")
            return redirect('eventos:detalle_evento', evento_id=evento.id)
    else:
        form = MensajeCorreoForm()

    return render(request, 'eventos/enviar_correo_inscritos.html', {'form': form, 'evento': evento})


@login_required
def estadisticas_por_usuario(request):
    today = date.today()

    mes_comienzo_temporada = settings.MES_COMIENZO_TEMPORADA

    if today.month < mes_comienzo_temporada:
        filtro_fecha = date(today.year - 1, mes_comienzo_temporada, 1)
    else:
        filtro_fecha = date(today.year, mes_comienzo_temporada, 1)

    reservas = Reserva.objects.filter(evento__fecha__gte=filtro_fecha)

    datos = (
        reservas.values('usuario__id', 'usuario__email', 'usuario__nombre')
        .annotate(eventos_inscritos=Count('evento', distinct=True))
        .order_by('-eventos_inscritos')
    )

    return render(request, 'eventos/estadisticas_usuarios.html', {
        'datos': datos,
        'desde': filtro_fecha,
    })


@login_required
def eventos_por_usuario(request, usuario_id):
    usuario = get_object_or_404(User, pk=usuario_id)
    reservas = Reserva.objects.filter(usuario=usuario).select_related("evento")

    return render(request, "eventos/eventos_por_usuario.html", {
        "usuario": usuario,
        "reservas": reservas
    })


@staff_member_required
def cancelar_reserva_admin(request, reserva_id):
    reserva = get_object_or_404(Reserva, id=reserva_id)
    usuario_id = reserva.usuario.id
    if request.method == "POST":
        reserva.delete()
    return redirect("eventos:eventos_por_usuario", usuario_id)
