"""
Vistas — App Appointments
============================
Calendario de citas, reservas, gestion de agenda.
"""

import json
import logging
from datetime import datetime, timedelta

from django.conf import settings
from django.contrib import messages
from django.contrib.auth.decorators import login_required
from django.core.exceptions import ValidationError
from django.core.mail import send_mail
from django.http import JsonResponse
from django.shortcuts import get_object_or_404, redirect, render
from django.template.loader import render_to_string
from django.utils import timezone
from django.views.decorators.http import require_GET, require_POST

from apps.users.decorators import doctora_required, paciente_required

from .models import BloqueoFecha, Cita, HorarioDoctora, Servicio
from .services import cambiar_estado_cita, crear_cita, obtener_slots_disponibles, validar_disponibilidad

logger = logging.getLogger("apps.appointments")


# ============================================================
# PAGINA PUBLICA DE CITAS (CALENDARIO)
# ============================================================
@login_required
def calendario_view(request):
    """Pagina principal de citas con FullCalendar."""
    servicios = Servicio.objects.filter(is_active=True)
    return render(request, "appointments/calendario.html", {
        "servicios": servicios,
    })


# ============================================================
# API — SLOTS DISPONIBLES
# ============================================================
@login_required
@require_GET
def api_slots_disponibles(request):
    """Retorna slots disponibles para una fecha y servicio."""
    fecha_str = request.GET.get("fecha")
    servicio_id = request.GET.get("servicio")

    if not fecha_str or not servicio_id:
        return JsonResponse({"error": "Data e serviço são obrigatórios."}, status=400)

    try:
        fecha = datetime.strptime(fecha_str, "%Y-%m-%d").date()
    except ValueError:
        return JsonResponse({"error": "Formato de fecha invalido."}, status=400)

    try:
        servicio = Servicio.objects.get(id=servicio_id, is_active=True)
    except Servicio.DoesNotExist:
        return JsonResponse({"error": "Serviço não encontrado."}, status=404)

    slots = obtener_slots_disponibles(fecha, servicio)
    return JsonResponse({"slots": slots})


# ============================================================
# API — EVENTOS PARA FULLCALENDAR
# ============================================================
@login_required
@require_GET
def api_eventos_calendario(request):
    """Retorna eventos para FullCalendar segun rol."""
    start = request.GET.get("start", "")
    end = request.GET.get("end", "")

    try:
        # FullCalendar envía +01:00, pero el "+" se decodifica como espacio en
        # query strings. Restauramos el "+" si fue convertido a espacio.
        start_clean = start.replace(" ", "+").replace("Z", "+00:00")
        end_clean = end.replace(" ", "+").replace("Z", "+00:00")
        start_date = datetime.fromisoformat(start_clean)
        end_date = datetime.fromisoformat(end_clean)
        # Con USE_TZ=False MySQL rechaza datetimes aware; convertir a naive.
        if not settings.USE_TZ:
            start_date = start_date.replace(tzinfo=None)
            end_date = end_date.replace(tzinfo=None)
    except (ValueError, AttributeError):
        return JsonResponse({"error": "Intervalo de datas inválido."}, status=400)

    user = request.user
    estados_visibles = [Cita.Estado.PENDIENTE, Cita.Estado.CONFIRMADA, Cita.Estado.COMPLETADA]

    if user.es_doctora or user.es_admin:
        citas = Cita.objects.filter(
            fecha_hora__gte=start_date,
            fecha_hora__lte=end_date,
            estado__in=estados_visibles,
            is_active=True,
        ).select_related("paciente", "servicio")
    else:
        citas = Cita.objects.filter(
            paciente=user,
            fecha_hora__gte=start_date,
            fecha_hora__lte=end_date,
            is_active=True,
        ).select_related("servicio")

    colores_estado = {
        "pendiente": "#F59E0B",
        "confirmada": "#10B981",
        "completada": "#6366F1",
        "cancelada": "#EF4444",
        "no_asistio": "#6B7280",
    }

    eventos = []
    for cita in citas:
        titulo = cita.servicio.nombre
        if user.es_doctora or user.es_admin:
            titulo = f"{cita.paciente.get_full_name()} — {titulo}"

        eventos.append({
            "id": str(cita.id),
            "title": titulo,
            "start": cita.fecha_hora.isoformat(),
            "end": cita.fecha_hora_fin.isoformat(),
            "backgroundColor": colores_estado.get(cita.estado, "#6B7280"),
            "borderColor": colores_estado.get(cita.estado, "#6B7280"),
            "extendedProps": {
                "estado": cita.estado,
                "servicio": cita.servicio.nombre,
                "paciente": cita.paciente.get_full_name() if (user.es_doctora or user.es_admin) else "",
                "notas": cita.notas_paciente,
            },
        })

    # Agregar bloqueos como eventos de fondo
    bloqueos = BloqueoFecha.objects.filter(
        fecha__gte=start_date.date(),
        fecha__lte=end_date.date(),
        is_active=True,
    )
    for bloqueo in bloqueos:
        eventos.append({
            "id": f"bloqueo-{bloqueo.id}",
            "title": bloqueo.motivo or "Não disponível",
            "start": bloqueo.fecha.isoformat(),
            "allDay": True,
            "display": "background",
            "backgroundColor": "#FEE2E2",
        })

    return JsonResponse(eventos, safe=False)


# ============================================================
# RESERVAR CITA (PACIENTE)
# ============================================================
@paciente_required
@require_POST
def reservar_cita_view(request):
    """Paciente reserva una nueva cita."""
    servicio_id = request.POST.get("servicio")
    fecha_str = request.POST.get("fecha")
    hora_str = request.POST.get("hora")
    notas = request.POST.get("notas", "").strip()

    if not all([servicio_id, fecha_str, hora_str]):
        return JsonResponse({"error": "Todos os campos são obrigatórios."}, status=400)

    try:
        servicio = Servicio.objects.get(id=servicio_id, is_active=True)
    except Servicio.DoesNotExist:
        return JsonResponse({"error": "Serviço não válido."}, status=400)

    try:
        fecha_hora_naive = datetime.strptime(f"{fecha_str} {hora_str}", "%Y-%m-%d %H:%M")
        if settings.USE_TZ:
            tz = timezone.get_current_timezone()
            fecha_hora = timezone.make_aware(fecha_hora_naive, tz)
        else:
            fecha_hora = fecha_hora_naive
    except ValueError:
        return JsonResponse({"error": "Data ou hora inválida."}, status=400)

    try:
        cita = crear_cita(
            paciente=request.user,
            servicio=servicio,
            fecha_hora=fecha_hora,
            notas=notas,
        )
        # Guardar especialidad si fue enviada
        especialidad = request.POST.get("especialidad", "").strip()
        if especialidad:
            cita.especialidad = especialidad
            cita.save(update_fields=["especialidad", "updated_at"])
    except ValidationError as e:
        return JsonResponse({"error": e.message}, status=400)

    # Enviar emails de notificacion
    _enviar_email_nueva_cita(request, cita)

    return JsonResponse({
        "success": True,
        "message": "Consulta agendada com sucesso.",
        "cita": {
            "id": str(cita.id),
            "servicio": cita.servicio.nombre,
            "fecha": cita.fecha_hora.strftime("%d/%m/%Y"),
            "hora": cita.fecha_hora.strftime("%H:%M"),
            "estado": cita.estado,
        },
    })


# ============================================================
# CANCELAR CITA (PACIENTE)
# ============================================================
@paciente_required
@require_POST
def cancelar_cita_view(request, cita_id):
    """Paciente cancela una cita."""
    cita = get_object_or_404(Cita, id=cita_id, paciente=request.user, is_active=True)

    if not cita.puede_cancelar:
        return JsonResponse({
            "error": "Não é possível cancelar esta consulta. A política requer no mínimo 24h de antecedência."
        }, status=400)

    try:
        cambiar_estado_cita(cita, Cita.Estado.CANCELADA, request.user, "Cancelada pelo paciente.")
    except ValidationError as e:
        return JsonResponse({"error": e.message}, status=400)

    return JsonResponse({"success": True, "message": "Consulta cancelada com sucesso."})


# ============================================================
# ACCIONES DOCTORA
# ============================================================
@doctora_required
@require_POST
def confirmar_cita_view(request, cita_id):
    """Doctora confirma una cita pendiente."""
    cita = get_object_or_404(Cita, id=cita_id, is_active=True)

    try:
        cambiar_estado_cita(cita, Cita.Estado.CONFIRMADA, request.user, "Confirmada pela doutora.")
    except ValidationError as e:
        return JsonResponse({"error": e.message}, status=400)

    _enviar_email_cita_confirmada(cita)

    return JsonResponse({"success": True, "message": "Consulta confirmada."})


@doctora_required
@require_POST
def completar_cita_view(request, cita_id):
    """Doctora marca cita como completada."""
    cita = get_object_or_404(Cita, id=cita_id, is_active=True)
    notas = request.POST.get("notas_doctora", "")

    if notas:
        cita.notas_doctora = notas
        cita.save(update_fields=["notas_doctora", "updated_at"])

    try:
        cambiar_estado_cita(cita, Cita.Estado.COMPLETADA, request.user, "Concluída pela doutora.")
    except ValidationError as e:
        return JsonResponse({"error": e.message}, status=400)

    return JsonResponse({"success": True, "message": "Consulta concluída."})


@doctora_required
@require_POST
def no_asistio_cita_view(request, cita_id):
    """Doctora marca que el paciente no asistio."""
    cita = get_object_or_404(Cita, id=cita_id, is_active=True)

    try:
        cambiar_estado_cita(cita, Cita.Estado.NO_ASISTIO, request.user, "Paciente não compareceu.")
    except ValidationError as e:
        return JsonResponse({"error": e.message}, status=400)

    return JsonResponse({"success": True, "message": "Marcada como não compareceu."})


@doctora_required
@require_POST
def cancelar_cita_doctora_view(request, cita_id):
    """Doctora cancela una cita."""
    cita = get_object_or_404(Cita, id=cita_id, is_active=True)
    motivo = request.POST.get("motivo", "Cancelada pela doutora.")

    try:
        cambiar_estado_cita(cita, Cita.Estado.CANCELADA, request.user, motivo)
    except ValidationError as e:
        return JsonResponse({"error": e.message}, status=400)

    _enviar_email_cita_cancelada(cita, motivo)

    return JsonResponse({"success": True, "message": "Consulta cancelada."})


# ============================================================
# GESTION HORARIOS (DOCTORA)
# ============================================================
@doctora_required
def horarios_view(request):
    """Doctora gestiona sus horarios semanales."""
    horarios = HorarioDoctora.objects.filter(is_active=True).order_by("dia_semana")
    bloqueos = BloqueoFecha.objects.filter(is_active=True, fecha__gte=timezone.now().date())
    return render(request, "appointments/horarios.html", {
        "horarios": horarios,
        "bloqueos": bloqueos,
    })


@doctora_required
@require_POST
def guardar_horario_view(request):
    """Doctora guarda/actualiza un horario."""
    dia = request.POST.get("dia_semana")
    hora_inicio = request.POST.get("hora_inicio")
    hora_fin = request.POST.get("hora_fin")

    if not all([dia, hora_inicio, hora_fin]):
        return JsonResponse({"error": "Todos os campos são obrigatórios."}, status=400)

    try:
        dia = int(dia)
        hora_inicio_t = datetime.strptime(hora_inicio, "%H:%M").time()
        hora_fin_t = datetime.strptime(hora_fin, "%H:%M").time()
    except (ValueError, TypeError):
        return JsonResponse({"error": "Dados inválidos."}, status=400)

    if hora_inicio_t >= hora_fin_t:
        return JsonResponse({"error": "A hora de início deve ser anterior à de fim."}, status=400)

    horario, created = HorarioDoctora.objects.update_or_create(
        dia_semana=dia,
        is_active=True,
        defaults={"hora_inicio": hora_inicio_t, "hora_fin": hora_fin_t, "activo": True},
    )

    return JsonResponse({
        "success": True,
        "message": f"Horário {'criado' if created else 'atualizado'}.",
    })


@doctora_required
@require_POST
def bloquear_fecha_view(request):
    """Doctora bloquea una fecha."""
    fecha_str = request.POST.get("fecha")
    motivo = request.POST.get("motivo", "")

    if not fecha_str:
        return JsonResponse({"error": "A data é obrigatória."}, status=400)

    try:
        fecha = datetime.strptime(fecha_str, "%Y-%m-%d").date()
    except ValueError:
        return JsonResponse({"error": "Formato de data inválido."}, status=400)

    if fecha < timezone.now().date():
        return JsonResponse({"error": "Não é possível bloquear uma data passada."}, status=400)

    bloqueo, created = BloqueoFecha.objects.get_or_create(
        fecha=fecha,
        defaults={"motivo": motivo},
    )

    if not created:
        return JsonResponse({"error": "Esta data já está bloqueada."}, status=400)

    return JsonResponse({"success": True, "message": f"Data {fecha} bloqueada."})


@doctora_required
@require_POST
def desbloquear_fecha_view(request, bloqueo_id):
    """Doctora desbloquea una fecha."""
    bloqueo = get_object_or_404(BloqueoFecha, id=bloqueo_id, is_active=True)
    bloqueo.soft_delete()
    return JsonResponse({"success": True, "message": "Data desbloqueada."})


# ============================================================
# API — DETALLE DE CITA
# ============================================================
@login_required
@require_GET
def api_detalle_cita(request, cita_id):
    """Retorna detalle de una cita para el modal."""
    cita = get_object_or_404(Cita, id=cita_id, is_active=True)

    # Solo el paciente dueno o la doctora/admin pueden ver
    if not (request.user == cita.paciente or request.user.es_doctora or request.user.es_admin):
        return JsonResponse({"error": "Não autorizado."}, status=403)

    data = {
        "id": str(cita.id),
        "paciente": cita.paciente.get_full_name(),
        "paciente_email": cita.paciente.email,
        "servicio": cita.servicio.nombre,
        "fecha": cita.fecha_hora.strftime("%d/%m/%Y"),
        "hora": cita.fecha_hora.strftime("%H:%M"),
        "duracion": cita.duracion_minutos,
        "estado": cita.estado,
        "estado_display": cita.get_estado_display(),
        "notas_paciente": cita.notas_paciente,
        "notas_doctora": cita.notas_doctora,
        "puede_cancelar": cita.puede_cancelar,
        "logs": [
            {
                "estado_anterior": log.estado_anterior,
                "estado_nuevo": log.estado_nuevo,
                "motivo": log.motivo,
                "fecha": log.fecha.strftime("%d/%m/%Y %H:%M"),
                "usuario": log.usuario.get_full_name() if log.usuario else "Sistema",
            }
            for log in cita.logs.all()[:10]
        ],
    }
    return JsonResponse(data)


# ============================================================
# HELPERS — EMAILS
# ============================================================
def _enviar_email_nueva_cita(request, cita):
    """Envia email de notificacion de nueva cita."""
    try:
        # Email al paciente
        send_mail(
            "A sua consulta foi solicitada - Dra. Mariela Moreno Autie",
            f"Olá {cita.paciente.nombre},\n\n"
            f"A sua consulta de {cita.servicio.nombre} para o "
            f"{cita.fecha_hora.strftime('%d/%m/%Y às %H:%M')} "
            f"foi registada.\n\n"
            f"Estado: Pendente de confirmação.\n"
            f"Iremos notificá-la quando for confirmada.\n\n"
            f"Dra. Mariela Moreno Autie - Ginecologia",
            settings.DEFAULT_FROM_EMAIL,
            [cita.paciente.email],
            fail_silently=True,
        )

        # Email a la doctora
        from django.contrib.auth import get_user_model
        User = get_user_model()
        doctoras = User.objects.filter(rol="doctora", is_active=True)
        for doctora in doctoras:
            send_mail(
                "Nova solicitação de consulta",
                f"Nova consulta solicitada:\n\n"
                f"Paciente: {cita.paciente.get_full_name()}\n"
                f"Serviço: {cita.servicio.nombre}\n"
                f"Data: {cita.fecha_hora.strftime('%d/%m/%Y às %H:%M')}\n"
                f"Notas: {cita.notas_paciente or 'Sem notas'}\n\n"
                f"Aceda ao painel para confirmar ou rejeitar.",
                settings.DEFAULT_FROM_EMAIL,
                [doctora.email],
                fail_silently=True,
            )
    except Exception:
        logger.exception("Error enviando email de nueva cita")


def _enviar_email_cita_confirmada(cita):
    """Envia email al paciente cuando la doctora confirma."""
    try:
        send_mail(
            "A sua consulta foi confirmada - Dra. Mariela Moreno Autie",
            f"Olá {cita.paciente.nombre},\n\n"
            f"A sua consulta de {cita.servicio.nombre} para o "
            f"{cita.fecha_hora.strftime('%d/%m/%Y às %H:%M')} "
            f"foi CONFIRMADA.\n\n"
            f"Esperamos por si.\n\n"
            f"Dra. Mariela Moreno Autie - Ginecologia",
            settings.DEFAULT_FROM_EMAIL,
            [cita.paciente.email],
            fail_silently=True,
        )
    except Exception:
        logger.exception("Error enviando email de cita confirmada")


def _enviar_email_cita_cancelada(cita, motivo):
    """Envia email al paciente cuando la doctora cancela/rechaza su cita."""
    try:
        send_mail(
            "A sua consulta foi cancelada - Dra. Mariela Moreno Autie",
            f"Olá {cita.paciente.nombre},\n\n"
            f"Lamentamos informar que a sua consulta de {cita.servicio.nombre} "
            f"agendada para o {cita.fecha_hora.strftime('%d/%m/%Y às %H:%M')} "
            f"foi cancelada.\n\n"
            f"Motivo: {motivo}\n\n"
            f"Pode agendar uma nova consulta a qualquer momento "
            f"a partir do seu painel de paciente.\n\n"
            f"Pedimos desculpa pelos inconvenientes.\n\n"
            f"Dra. Mariela Moreno Autie - Ginecologia",
            settings.DEFAULT_FROM_EMAIL,
            [cita.paciente.email],
            fail_silently=True,
        )
    except Exception:
        logger.exception("Error enviando email de cita cancelada")


# ============================================================
# API — ESPECIALIDADES POR SERVICIO
# ============================================================
@login_required
@require_GET
def api_especialidades(request):
    """
    Retorna las especialidades (hijos) de un servicio dado.
    Busca en ServicioMedico, Laboratorio, Imagen, Proceder, Cirugia
    que tengan servicio_padre = servicio_id.
    """
    servicio_id = request.GET.get("servicio_id")
    if not servicio_id:
        return JsonResponse({"especialidades": []})

    from apps.finance.models import Cirugia, ControlPrenatal, Imagen, Laboratorio, Proceder, ServicioMedico

    resultados = []
    for modelo in [ServicioMedico, Laboratorio, Imagen, Proceder, Cirugia, ControlPrenatal]:
        items = modelo.objects.filter(
            servicio_padre_id=servicio_id, is_active=True
        ).values("id", "nombre")
        for item in items:
            resultados.append({
                "id": str(item["id"]),
                "nombre": item["nombre"],
            })

    # Ordenar por nombre
    resultados.sort(key=lambda x: x["nombre"])
    return JsonResponse({"especialidades": resultados})
