from usuarios.permissions import IsClubAdmin, IsClubStaff
import json

from django.conf import settings
from django.db import transaction
from django.shortcuts import render
from django.utils import timezone
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema
from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import AllowAny, IsAuthenticated
from rest_framework.response import Response

from pywebpush import webpush, WebPushException

from avisos.models import Aviso, AvisoDestinatario, PushSuscriptions
from usuarios.models import Usuarios


# Create your views here.
@swagger_auto_schema(
    method='get',
    operation_description="Obtener los avisos",
    tags=["Avisos"],
    security=[{'Bearer': []}],
    responses={
        200: openapi.Response(
            description="Lista de avisos"
        ),
        400: openapi.Response(description="Ocurrió un error al listar equipos"),
    },
)
@api_view(['GET'])
@permission_classes([IsAuthenticated])
def listarAvisos(request):
    avisos = Aviso.objects.select_related("creado_por", "creado_por__tipo").order_by("-fecha_creacion")

    data = [
        {
            "id": aviso.id,
            "titulo": aviso.titulo,
            "contenido": aviso.contenido,
            "creado_por": aviso.creado_por_id,
            "creado_por_nombre": getattr(aviso.creado_por, "name", None),
            "creado_por_rol": getattr(getattr(aviso.creado_por, "tipo", None), "description", None),
            "fecha_creacion": aviso.fecha_creacion
        }
        for aviso in avisos
    ]
    return Response(data, status=status.HTTP_200_OK)

@swagger_auto_schema(
    method='post',
    operation_description=(
        "Agregar un nuevo aviso y generar destinatarios (AvisoDestinatario). "
        "Permite enviar a usuarios sueltos o a todos."
    ),
    tags=["Avisos"],
    security=[{'Bearer': []}],
    request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            "titulo": openapi.Schema(
                type=openapi.TYPE_STRING,
                description="Título del aviso"
            ),
            "contenido": openapi.Schema(
                type=openapi.TYPE_STRING,
                description="Contenido del aviso"
            ),

            # Recomendado: el backend toma el creador desde request.user.
            # Lo dejo opcional como fallback si aún lo usas.
            "creado_por": openapi.Schema(
                type=openapi.TYPE_INTEGER,
                description="(Opcional) ID del usuario creador. Recomendado NO enviarlo y usar request.user."
            ),

            "target_all": openapi.Schema(
                type=openapi.TYPE_BOOLEAN,
                description="Si es true, el aviso se envía a todos los usuarios."
            ),
            "user_ids": openapi.Schema(
                type=openapi.TYPE_ARRAY,
                description="IDs de usuarios destinatarios (usuarios sueltos).",
                items=openapi.Items(type=openapi.TYPE_INTEGER),
            )
        },
        required=["titulo", "contenido"],
        description=(
            "Debes indicar al menos un criterio de destinatarios: "
            "`target_all=true` o `user_ids`."
        ),
        example={
            "titulo": "Mantenimiento",
            "contenido": "El sistema estará en mantenimiento a las 22:00",
            "target_all": False,
            "user_ids": [12, 15]
        }
    ),
    responses={
        201: openapi.Response(description="Aviso creado exitosamente (incluye cantidad de destinatarios creados)."),
        400: openapi.Response(description="Solicitud inválida / faltan destinatarios / payload incorrecto."),
        401: openapi.Response(description="No autenticado."),
        500: openapi.Response(description="Error interno."),
    },
)

@api_view(['POST'])
@permission_classes([IsClubAdmin])
def addAviso(request):
    """
    Crea un aviso y genera los destinatarios (AvisoDestinatario) según:
      - target_all: bool
      - user_ids: [int]
      - group_ids: [int]   (Django auth Group)
      - role_ids: [int]    (si roles == groups, puedes reutilizar group_ids)
    """
    try:
        body = json.loads(request.body or "{}")

        titulo = body.get("titulo")
        contenido = body.get("contenido")

        # Destinatarios (opcionales pero al menos uno debería venir)
        target_all = bool(body.get("target_all", False))
        user_ids = body.get("user_ids") or []

        if not titulo or not contenido:
            return Response({"message": "titulo y contenido son obligatorios"}, status=400)

        # Mejor: no aceptar creado_por desde el cliente (evita suplantación)
        # Si tu auth setea request.user como instancia de Usuarios, esto funciona.
        # Si request.user es un User de Django distinto, ajusta según tu proyecto.
        creador = getattr(request, "user", None)
        print(creador)
        if not creador or not isinstance(creador, Usuarios):
            # Fallback: si aún dependes de creado_por desde el body
            creado_por = body.get("creado_por")
            if not creado_por:
                return Response({"message": "creado_por es obligatorio"}, status=400)
            try:
                creador = Usuarios.objects.get(id=creado_por)
            except Usuarios.DoesNotExist:
                return Response({"message": "creado_por inválido"}, status=400)

        # Validación: debe haber algún criterio de destinatarios
        if not target_all and not user_ids:
            return Response(
                {"message": "Debes indicar destinatarios: target_all, user_ids, group_ids o role_ids"},
                status=400
            )

        with transaction.atomic():
            aviso = Aviso.objects.create(
                titulo=titulo,
                contenido=contenido,
                creado_por=creador
            )

            # 1) resolver destinatarios a una lista de IDs sin duplicados
            recipient_ids = set()

            if target_all:
                recipient_ids.update(
                    Usuarios.objects.values_list("id", flat=True)
                )

            if user_ids:
                recipient_ids.update(
                    Usuarios.objects.filter(id__in=user_ids).values_list("id", flat=True)
                )

            if not recipient_ids:
                return Response({"message": "No se encontraron destinatarios válidos"}, status=400)

            now = timezone.now()
            destinatarios = [
                AvisoDestinatario(
                    idUser_id=user_id,
                    idAviso=aviso,
                    fecha_envio=now,
                    fecha_leido=None
                )
                for user_id in recipient_ids
            ]
            AvisoDestinatario.objects.bulk_create(destinatarios)
            print(recipient_ids)
            print(aviso)
            metrics = send_push_to_users(recipient_ids, build_aviso_payload(aviso))

        return Response({
            "message": "Aviso creado con éxito",
            "aviso": {
                "id": aviso.id,
                "titulo": aviso.titulo,
                "contenido": aviso.contenido,
                "creado_por": aviso.creado_por_id,
                "fecha_creacion": aviso.fecha_creacion,
                "destinatarios_creados": len(recipient_ids)
            },
            "metrics": metrics
        }, status=201)

    except json.JSONDecodeError:
        return Response({"error": "Solicitud inválida"}, status=400)
    except Exception as e:
        return Response({"error": "Error interno", "detail": str(e)}, status=500)


@swagger_auto_schema(
    method='put',
    operation_description="Actualizar un aviso por ID",
    tags=["Avisos"],
    security=[{'Bearer': []}],
    request_body=openapi.Schema(
        type=openapi.TYPE_OBJECT,
        properties={
            "titulo": openapi.Schema(type=openapi.TYPE_STRING, description="Tìtulo del aviso"),
            "contenido": openapi.Schema(type=openapi.TYPE_STRING, description="Contenido del aviso"),
            "creado_por": openapi.Schema(type=openapi.TYPE_STRING, description="Creador del aviso")
        },
    ),
    responses={
        200: openapi.Response(description="Aviso actualizado exitosamente"),
        404: openapi.Response(description="Aviso no encontrado"),
    },
)
@api_view(['PUT'])
@permission_classes([IsClubAdmin])
def editAviso(request,id):
    try:
        aviso = Aviso.objects.get(id=id)
        body = json.loads(request.body)

        if "titulo" in body:
            aviso.titulo = body["titulo"]
        if "contenido" in body:
            aviso.contenido = body["contenido"]
        if "creado_por" in body:
            try:
                user = Usuarios.objects.get(id=body["creado_por"])
                aviso.creado_por = user
            except:
                return Response({"error": "El usuario no existe"}, status=status.HTTP_404_NOT_FOUND)

        aviso.save()
        return Response({
            "message": "Aviso actualizado con éxito",
            "aviso": {
                "id": aviso.id,
                "titulo": aviso.titulo,
                "contenido": aviso.contenido,
                "creado_por": aviso.creado_por_id
            }
        }, status=status.HTTP_200_OK)
    except Aviso.DoesNotExist:
        return Response({"error": "Aviso no encontrado"}, status=status.HTTP_404_NOT_FOUND)
    except json.JSONDecodeError:
        return Response({"error": "Solicitud inválida"}, status=status.HTTP_400_BAD_REQUEST)


@swagger_auto_schema(
    method='delete',
    operation_description="Eliminar un aviso por ID",
    tags=["Avisos"],
    security=[{'Bearer': []}],
    responses={
        200: openapi.Response(description="Usuario eliminado con éxito"),
        404: openapi.Response(description="Aviso no encontrado"),
    },
)
@api_view(['DELETE'])
@permission_classes([IsClubAdmin])
def deleteAviso(request, id):
    try:
        aviso = Aviso.objects.get(id=id)
        aviso.delete()
        return Response({"message": "Aviso eliminado con éxito"}, status=status.HTTP_200_OK)
    except Aviso.DoesNotExist:
        return Response({"error": "Aviso no encontrado"}, status=status.HTTP_404_NOT_FOUND)

@api_view(["GET"])
@permission_classes([IsAuthenticated])
def mis_avisos(request, user, unread):
    idUser = user
    unread = unread

    if not idUser:
        return Response({"message": "Parámetro 'user' es obligatorio"}, status=400)

    qs = (
        AvisoDestinatario.objects
        .select_related("idAviso", "idAviso__creado_por")
        .filter(idUser_id=idUser)  # usa idUser_id para evitar comparar FK con string
        .order_by("-idAviso__fecha_creacion")
    )

    if unread is not None:
        unread_str = str(unread).strip().lower()
        if unread_str in ("1", "true", "yes"):
            qs = qs.filter(fecha_leido__isnull=True)   # NO leídos
        elif unread_str in ("0", "false", "no"):
            qs = qs.filter(fecha_leido__isnull=False)  # SÍ leídos
        else:
            return Response({"message": "Parámetro 'unread' inválido (use true/false)"}, status=400)

    data = []
    for ad in qs:
        aviso = ad.idAviso
        data.append({
            "id": aviso.id,
            "titulo": aviso.titulo,
            "contenido": aviso.contenido,
            "creado_por": getattr(aviso.creado_por, "id", None),
            "fecha_creacion": aviso.fecha_creacion,
            "fecha_envio": ad.fecha_envio,
            "fecha_leido": ad.fecha_leido,
        })

    return Response({"results": data}, status=200)


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def unread_count(request, user):
    """
    GET /api/avisos/unread-count

    Devuelve cantidad de avisos no leídos del usuario autenticado.
    """
    idUser = user
    count = AvisoDestinatario.objects.filter(
        idUser=idUser,
        fecha_leido__isnull=True
    ).count()

    return Response({"unread": count}, status=200)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def marcar_leido(request, aviso_id: int):
    """
    POST /api/avisos/{aviso_id}/read

    Marca como leído el aviso para el usuario autenticado.
    """
    user = request.user
    now = timezone.now()

    updated = AvisoDestinatario.objects.filter(
        idUser=user,
        idAviso_id=aviso_id,
        fecha_leido__isnull=True
    ).update(fecha_leido=now)

    if updated == 0:
        # Puede ser: no existe relación, o ya estaba leído
        exists = AvisoDestinatario.objects.filter(idUser=user, idAviso_id=aviso_id).exists()
        if not exists:
            return Response({"message": "Aviso no encontrado para este usuario"}, status=404)
        return Response({"message": "El aviso ya estaba marcado como leído"}, status=200)

    return Response({"message": "Aviso marcado como leído", "fecha_leido": now}, status=200)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def marcar_todos_leidos(request):
    """
    POST /api/avisos/read-all

    Marca todos los avisos como leídos para el usuario autenticado.
    """
    body = json.loads(request.body)
    user = Usuarios.objects.get(id=body.get("idUser"))
    now = timezone.now()

    updated = AvisoDestinatario.objects.filter(
        idUser=user,
        fecha_leido__isnull=True
    ).update(fecha_leido=now)

    return Response({"message": "OK", "updated": updated, "fecha_leido": now}, status=200)


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def push_subscribe(request):
    """
    POST /api/push/subscribe
    Body:
    {
      "endpoint": "...",
      "keys": { "p256dh": "...", "auth": "..." }
    }
    """
    try:
        body = json.loads(request.body or "{}")
    except json.JSONDecodeError:
        return Response({"message": "JSON inválido"}, status=400)

    idUser = body.get("idUser")
    endpoint = body.get("endpoint")
    keys = body.get("keys") or {}
    p256dh = keys.get("p256dh")
    auth = keys.get("auth")
    user = Usuarios.objects.get(id=idUser)

    if not endpoint or not p256dh or not auth:
        return Response({"message": "endpoint y keys(p256dh, auth) son obligatorios"}, status=400)

    obj, created = PushSuscriptions.objects.update_or_create(
        endpoint=endpoint,
        defaults={
            "idUser": user,
            "p256dh": p256dh,
            "auth": auth,
            "last_seen_at": timezone.now(),
        },
    )

    return Response({"ok": True, "created": created, "id": obj.id}, status=201 if created else 200)


@api_view(["DELETE"])
@permission_classes([IsAuthenticated])
def push_unsubscribe(request):
    """
    DELETE /api/push/unsubscribe
    Body:
    { "endpoint": "..." }
    """
    try:
        body = json.loads(request.body or "{}")
    except json.JSONDecodeError:
        return Response({"message": "JSON inválido"}, status=400)

    endpoint = body.get("endpoint")
    idUser = body.get("idUser")
    user = Usuarios.objects.get(id=idUser)
    if not endpoint:
        return Response({"message": "endpoint es obligatorio"}, status=400)

    deleted, _ = PushSuscriptions.objects.filter(idUser=user, endpoint=endpoint).delete()
    return Response({"ok": True, "deleted": deleted}, status=200)

def send_push_to_users(user_ids, payload: dict) -> dict:
    """
    user_ids: iterable[int]
    payload: dict -> se serializa a JSON y llega al SW
    """
    print(list(user_ids))
    subs = PushSuscriptions.objects.filter(idUser_id__in=user_ids)
    print(list(subs))
    sent = 0
    deleted = 0
    failed = 0

    for s in subs:
        subscription_info = {
            "endpoint": s.endpoint,
            "keys": {"p256dh": s.p256dh, "auth": s.auth},
        }

        print("suscripction_info", subscription_info)

        try:
            webpush(
                subscription_info=subscription_info,
                data=json.dumps(payload),
                vapid_private_key=settings.WEBPUSH_VAPID_PRIVATE_KEY,
                vapid_claims={"sub": settings.WEBPUSH_VAPID_SUBJECT},
            )
            sent += 1
            if hasattr(s, "last_seen_at"):
                s.last_seen_at = timezone.now()
                s.save(update_fields=["last_seen_at"])

        except WebPushException as ex:
            status = getattr(ex.response, "status_code", None)
            print("WebPushException status:", status, "endpoint:", s.endpoint)
            print(str(ex))
            # 404/410: la subscription ya no existe (usuario la quitó / cambió navegador)
            if status in (404, 410):
                s.delete()
                deleted += 1
            else:
                failed += 1
        except Exception:
            failed += 1

    return {"subs": subs.count(), "sent": sent, "deleted": deleted, "failed": failed}

def build_aviso_payload(aviso) -> dict:
    return {
        "type": "aviso",
        "avisoId": aviso.id,
        "title": aviso.titulo,
        "body": aviso.contenido,
        "url": "avisos",
    }