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
import requests
import os

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, ReservaForm

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

User = get_user_model()

logger = logging.getLogger(__name__)

BOT_TOKEN = os.getenv("TELEGRAM_BOT_TOKEN",  "8604655521:AAEREJsenslfLyZ4vpNs475BTI6rboJ9UY0")
CHAT_ID = os.getenv("TELEGRAM_CHAT_ID", "-1003558251921")  # chat_id del canal JugarAlPadel

# helper for sending reservation emails

def enviar_correo_reserva(evento, usuario, acompanante=None):
    """Envía los correos de confirmación al usuario (y acompañante) y notificación al admin.

    Parameters
    ----------
    evento : Evento
        Instancia del evento reservado.
    usuario : User
        Usuario que realiza la reserva.
    acompanante : User | None
        Si se trata de un evento para parejas, usuario acompañante opcional.
    """
    from_email = settings.DEFAULT_FROM_EMAIL
    to_email = usuario.email
    cc_email = []
    # cc siempre al administrador; al acompañante si existe
    # cc_email = [settings.ADMIN_EMAIL]
    if acompanante and acompanante.email:
        cc_email.append(acompanante.email)

    # cuerpo de texto plano
    texto = f'Hola {usuario.nombre}, \n\n'
    texto += f'Te has inscrito correctamente en el evento "{evento.nombre}".\n'
    texto += f'Detalles del evento:\n'
    texto += f'Nombre: {evento.nombre}\n'
    texto += f'Descripción: {evento.descripcion}\n'
    texto += f'Fecha: {evento.fecha.strftime("%d/%b/%y")}  a las {evento.hora}\n'
    texto += f'Precio: {evento.precio} €'

    if acompanante:
        texto += f"\nHas indicado como pareja a {acompanante.nombre}.\n"

    texto += '\n¡Gracias por inscribirte!\n\n'
    texto += '\n\nIMPORTANTE: Para gestionar cambios o bajas NO contestes a este correo, escribe al administrador por WhatsApp\n\n'

    # plantilla HTML adecuada
    if acompanante:
        html_content = render_to_string(
            'emails/confirmacion_reserva_parejas.html',
            {'evento': evento, 'usuario': usuario, 'acompanante': acompanante}
        )
    else:
        html_content = render_to_string(
            'emails/confirmacion_reserva.html',
            {'evento': evento, 'usuario': usuario}
        )

    msg = EmailMultiAlternatives(
        f'Confirmación de Reserva para {evento.nombre}',
        texto,
        from_email,
        [to_email],
        cc=cc_email
    )
    msg.attach_alternative(html_content, "text/html")
    msg.send()

    # notificación al administrador (se envía siempre incluso si ya está
    # en cc porque el formato es distinto y queremos consolidar datos de plazas)
    asunto_admin = f'[{evento.plazas_restantes()}/{evento.plazas_disponibles}] Nueva Reserva para {evento.nombre}'
    mensaje_admin = (
        f'El usuario {usuario.nombre} ({usuario.email}) se ha inscrito en el evento "{evento.nombre}".\n'
        f'Quedan {evento.plazas_restantes()} plazas disponibles.'
    )
    html_content_admin = render_to_string(
        'emails/notificacion_reserva_admin.html',
        {
            'evento': evento,
            'usuario': usuario,
            'acompanante': acompanante,
            'plazas_restantes': evento.plazas_restantes(),
            'plazas_disponibles': evento.plazas_disponibles,
        },
    )
    email_admin = EmailMultiAlternatives(
        asunto_admin,
        mensaje_admin,
        settings.DEFAULT_FROM_EMAIL,
        [settings.ADMIN_EMAIL],
    )
    email_admin.attach_alternative(html_content_admin, "text/html")
    email_admin.send()



def enviar_correo_lista_espera(evento, usuario, acompanante=None):
    """Envía correos cuando un usuario se apunta a la lista de espera.

    Parameters
    ----------
    evento : Evento
    usuario : User
    acompanante : User | None
    """
    from_email = settings.DEFAULT_FROM_EMAIL
    to_email = usuario.email
    cc_email = []
    if acompanante and acompanante.email:
        cc_email.append(acompanante.email)

    texto = f'Hola {usuario.nombre},\n\n'
    texto += f'Te has añadido a la lista de espera del evento "{evento.nombre}".\n'
    texto += f'Detalles del evento:\n'
    texto += f'Nombre: {evento.nombre}\n'
    texto += f'Descripción: {evento.descripcion}\n'
    texto += f'Fecha: {evento.fecha.strftime("%d/%b/%y")}  a las {evento.hora}\n'

    if acompanante:
        texto += f"\nHas indicado como pareja a {acompanante.nombre}.\n"

    texto += '\nTe avisaremos si se libera alguna plaza.\n\n'
    texto += '\n\nIMPORTANTE: Para gestionar cambios o bajas NO contestes a este correo, escribe al administrador por WhatsApp\n\n'

    if acompanante:
        html = render_to_string(
            'emails/confirmacion_lista_espera_parejas.html',
            {'evento': evento, 'usuario': usuario, 'acompanante': acompanante},
        )
    else:
        html = render_to_string(
            'emails/confirmacion_lista_espera.html',
            {'evento': evento, 'usuario': usuario},
        )

    subject = f'Confirmación lista de espera para {evento.nombre}'
    msg = EmailMultiAlternatives(subject, texto, from_email, [to_email], cc=cc_email)
    msg.attach_alternative(html, 'text/html')
    msg.send()

    # notificar al admin
    asunto_admin = f'Nueva lista de espera para {evento.nombre}'
    mensaje_admin = (
        f'El usuario {usuario.nombre} ({usuario.email}) se ha añadido a la lista de espera.'
    )
    if acompanante:
        mensaje_admin += f' Acompañante: {acompanante.nombre} ({acompanante.email}).'
    html_admin = render_to_string(
        'emails/notificacion_lista_espera_admin.html',
        {'evento': evento, 'usuario': usuario, 'acompanante': acompanante},
    )
    email_admin2 = EmailMultiAlternatives(
        asunto_admin,
        mensaje_admin,
        settings.DEFAULT_FROM_EMAIL,
        [settings.ADMIN_EMAIL],
    )
    email_admin2.attach_alternative(html_admin, 'text/html')
    email_admin2.send()


@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, iniciador=True)

        # separar el envío de correos a función reutilizable
        enviar_correo_reserva(evento, request.user)

        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')


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

    # Si ya está inscrito
    if Reserva.objects.filter(evento=evento, usuario=request.user).exists():
        messages.warning(request, "Ya estás inscrito en este evento.")
        return redirect('eventos:detalle_evento', evento_id=evento.id)

    if request.method == 'POST':
        form = ReservaForm(request.POST, evento=evento, usuario=request.user)
        if form.is_valid():
            acompanante = form.cleaned_data.get('acompanante')

            # Crear la reserva principal
            Reserva.objects.create(
                evento=evento,
                usuario=request.user,
                iniciador=True,
                acompanante=acompanante if evento.para_parejas else None
            )

            # Si es un evento para parejas, crear también la reserva del acompañante
            if evento.para_parejas and acompanante:
                # Solo si el acompañante no está ya inscrito
                if not Reserva.objects.filter(evento=evento, usuario=acompanante).exists():
                    Reserva.objects.create(
                        evento=evento,
                        usuario=acompanante,
                        iniciador=False,
                        acompanante=request.user
                    )

            # Enviar correos de confirmación/aviso usando la función reutilizable
            enviar_correo_reserva(evento, request.user, acompanante=acompanante if evento.para_parejas else None)

            messages.success(request, "Reserva realizada correctamente.")
            return redirect('eventos:detalle_evento', evento_id=evento.id)
    else:
        form = ReservaForm(evento=evento, usuario=request.user)

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


@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()

        # Envía mensajes al canal de Telegram
        if evento.para_parejas:
            send_photo_url("https://jugaralpadel.es/static/images/parejas.png", "Pozo %s publicado: https://jugaralpadel.es/eventos/eventos/%s/" % (evento.nombre, evento.id)) 
        else:
            send_photo_url("https://jugaralpadel.es/static/images//padel-court.jpg", "Pozo %s publicado: https://jugaralpadel.es/eventos/eventos/%s/" % (evento.nombre, evento.id)) 

        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,
            'para_parejas': evento.para_parejas
        })

    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,
            'hora': evento.hora,
            'precio': evento.precio,
            '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,
            'para_parejas': evento.para_parejas,
            'lugar': evento.lugar
        }
    )

    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)
                # enviar correos de confirmación y aviso al administrador
                enviar_correo_lista_espera(evento, 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})


@login_required
def apuntar_lista_espera_parejas(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, evento=evento, usuario=request.user)
        if form.is_valid():
            acompanante = form.cleaned_data.get('acompanante')

            ListaEspera.objects.create(
                evento=evento, 
                usuario=request.user,
                acompanante=acompanante if evento.para_parejas else None
            )
            # enviar correos de confirmación y aviso al administrador
            enviar_correo_lista_espera(evento, request.user,
                                       acompanante=acompanante if evento.para_parejas else None)
            messages.success(request, 'Te has apuntado a la lista de espera para este evento.')
            return redirect('eventos:lista_eventos')
    else:
        form = ListaEsperaForm(evento=evento, usuario=request.user)

    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


# Enviar correo a los inscritos en formato html

@user_passes_test(es_admin)  # Solo administradores pueden acceder
def enviar_correo_inscritos_html(request, evento_id):
    """
    Envía un correo HTML a los inscritos en un evento
    """

    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}"

            from_email = settings.DEFAULT_FROM_EMAIL

            for usuario in destinatarios:
                html_content = render_to_string(
                    'emails/correo_a_inscritos.html',
                    {'evento': evento, 
                    'usuario': usuario, 
                    'mensaje_usuario': mensaje_usuario, 
                    'asunto_usuario': asunto_usuario}
                )

                msg = EmailMultiAlternatives(
                    asunto,
                    mensaje_usuario,
                    from_email,
                    [usuario],
                )
                msg.attach_alternative(html_content, "text/html")
                msg.send()

            messages.success(request, f"Correo enviado a {len(destinatarios)} inscritos.")
            return redirect('eventos:detalle_evento', evento_id=evento.id)
            
    else:
        # Crear texto por defecto con los datos del evento (formato HTML)
        asunto_defecto = f"Información sobre {evento.nombre}"
        mensaje_defecto = f"""<p>Hola a todos,</p>

<p>Os escribimos para daros información sobre el pozo:</p>

<ul>
    <li><strong>Descripción:</strong> {evento.nombre}</li>
    <li><strong>Fecha:</strong> {evento.fecha.strftime('%d/%m/%Y')} a las {evento.hora}</li>
    <li><strong>Precio:</strong> {evento.precio} € por persona</li>
</ul>
"""
        
        if evento.lugar:
            mensaje_defecto += f'<p><strong>Lugar:</strong> <a href="{ evento.lugar.url_ubicacion }" target="_blank">{ evento.lugar.nombre }</a> ({ evento.lugar.notas })</p>'

        mensaje_defecto += "<p>Por favor se ruega y se pide <strong>puntualidad</strong>.</p>"
        
        form = MensajeCorreoForm(initial={
            'asunto': asunto_defecto,
            'mensaje': mensaje_defecto
        })

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


# Muestra las estadísticas por usuario

@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')
    )

    usuarios = User.objects.all().annotate(
        eventos_inscritos=Count(
            'reserva__evento', 
            filter=Q(reserva__evento__fecha__gte=filtro_fecha), 
            distinct=True)
    ).order_by('-eventos_inscritos')

    return render(request, 'eventos/estadisticas_usuarios.html', {
        'datos': usuarios,
        '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)


# Función para enviar mensaje a canal

def send_message(text):
    url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
    params = {
        "chat_id": CHAT_ID,
        "text": text,
    }
    response = requests.get(url, params=params)
    return response.json()

# Función para enviar mensaje al canal, con foto

def send_photo_url(photo_url, caption=None):
    url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendPhoto"
    params = {
        "chat_id": CHAT_ID,
        "photo": photo_url,
    }
    if caption:
        params["caption"] = caption
    response = requests.get(url, params=params)
    return response.json()
