"""
Vistas de autenticación y paneles — App Users
===============================================
Registro · Login · Logout · Confirmación email · Password reset · Dashboards por rol.
"""

import logging

from urllib.parse import urlparse

from django.conf import settings
from django.contrib import messages
from django.contrib.auth import authenticate, get_user_model, login, logout
from django.contrib.auth.decorators import login_required
from django.contrib.auth.views import (
    PasswordResetCompleteView,
    PasswordResetConfirmView,
    PasswordResetDoneView,
    PasswordResetView,
)
from django.shortcuts import redirect, render
from django.template.loader import render_to_string
from django.urls import reverse, reverse_lazy
from django.utils.encoding import force_bytes, force_str
from django.utils.http import url_has_allowed_host_and_scheme, urlsafe_base64_decode, urlsafe_base64_encode
from django.views.decorators.http import require_POST

from apps.core.models import LoginAttempt
from apps.core.ratelimits import ratelimit_login, ratelimit_register

from .decorators import admin_required, doctora_required, paciente_required
from .forms import (
    CustomPasswordResetForm,
    CustomSetPasswordForm,
    EditarPerfilForm,
    LoginForm,
    RegistroForm,
)
from .tokens import email_confirmation_token

logger = logging.getLogger("apps.users")
User = get_user_model()


# ============================================================
# REGISTRO
# ============================================================
@ratelimit_register
def registro_view(request):
    """Registro de nuevo paciente con envío de email de confirmación."""
    if request.user.is_authenticated:
        return redirect(_get_dashboard_url(request.user))

    if request.method == "POST":
        form = RegistroForm(request.POST, request.FILES)
        if form.is_valid():
            user = form.save()
            try:
                _send_confirmation_email(request, user)
                messages.success(
                    request,
                    "Registo efetuado com sucesso! Verifique o seu email para confirmar a sua conta.",
                )
            except Exception:
                logger.exception("Error enviando email de confirmación a %s", user.email)
                messages.warning(
                    request,
                    "Registo efetuado com sucesso! Mas houve um problema ao enviar o email. Contacte o suporte.",
                )
            return redirect("users:login")
    else:
        form = RegistroForm()

    return render(request, "auth/register.html", {"form": form})


# ============================================================
# CONFIRMACIÓN DE EMAIL
# ============================================================
def confirmar_email_view(request, uidb64, token):
    """Confirma el email del usuario mediante token."""
    try:
        uid = force_str(urlsafe_base64_decode(uidb64))
        user = User.objects.get(pk=uid)
    except (TypeError, ValueError, OverflowError, User.DoesNotExist):
        user = None

    if user is not None and email_confirmation_token.check_token(user, token):
        user.is_verified = True
        user.save(update_fields=["is_verified"])
        messages.success(request, "Email confirmado com sucesso! Já pode iniciar sessão.")
        return redirect("users:login")

    messages.error(request, "O link de confirmação é inválido ou expirou.")
    return redirect("users:login")


# ============================================================
# LOGIN
# ============================================================
@ratelimit_login
def login_view(request):
    """Login con email/contraseña y redirección según rol."""
    if request.user.is_authenticated:
        return redirect(_get_dashboard_url(request.user))

    if request.method == "POST":
        form = LoginForm(request.POST)
        if form.is_valid():
            email = form.cleaned_data["email"].lower().strip()
            password = form.cleaned_data["password"]

            # Verificar bloqueo antes de intentar
            if LoginAttempt.is_locked(email):
                lockout_min = getattr(settings, "ACCOUNT_LOGIN_LOCKOUT_MINUTES", 30)
                messages.error(
                    request,
                    f"Conta bloqueada por tentativas falhadas. Tente em {lockout_min} minutos.",
                )
                return render(request, "auth/login.html", {"form": form})

            user = authenticate(request, username=email, password=password)

            if user is not None:
                if not user.is_verified:
                    messages.warning(
                        request,
                        "Deve confirmar o seu email antes de iniciar sessão. Verifique a sua caixa de entrada.",
                    )
                    return render(request, "auth/login.html", {"form": form})

                if not user.is_active:
                    messages.error(request, "A sua conta foi desativada.")
                    return render(request, "auth/login.html", {"form": form})

                login(request, user)
                logger.info("Login exitoso: %s (rol: %s)", user.email, user.rol)
                next_url = request.GET.get("next") or request.POST.get("next")
                if next_url and not url_has_allowed_host_and_scheme(
                    next_url, allowed_hosts={request.get_host()}, require_https=request.is_secure()
                ):
                    next_url = None
                return redirect(next_url or _get_dashboard_url(user))
            else:
                messages.error(request, "Email ou senha incorretos.")
    else:
        form = LoginForm()

    return render(request, "auth/login.html", {"form": form})


# ============================================================
# LOGOUT
# ============================================================
@require_POST
@login_required
def logout_view(request):
    """Cierra sesión e invalida, redirige a home."""
    logout(request)
    messages.info(request, "Sessão encerrada com sucesso.")
    return redirect("home")


# ============================================================
# PASSWORD RESET (Class-Based Views de Django)
# ============================================================
class CustomPasswordResetView(PasswordResetView):
    template_name = "auth/password_reset.html"
    email_template_name = "email/password_reset_email.html"
    subject_template_name = "email/password_reset_subject.txt"
    form_class = CustomPasswordResetForm
    success_url = reverse_lazy("users:password_reset_done")

    @property
    def extra_email_context(self):
        parsed = urlparse(settings.SITE_URL)
        return {
            "protocol": parsed.scheme,
            "domain": parsed.netloc,
        }


class CustomPasswordResetDoneView(PasswordResetDoneView):
    template_name = "auth/password_reset_done.html"


class CustomPasswordResetConfirmView(PasswordResetConfirmView):
    template_name = "auth/password_reset_confirm.html"
    form_class = CustomSetPasswordForm
    success_url = reverse_lazy("users:password_reset_complete")


class CustomPasswordResetCompleteView(PasswordResetCompleteView):
    template_name = "auth/password_reset_complete.html"


# ============================================================
# EDITAR PERFIL
# ============================================================
@login_required
def editar_perfil_view(request):
    """Permite al usuario editar su nombre, apellido, teléfono y foto."""
    if request.method == "POST":
        form = EditarPerfilForm(request.POST, request.FILES, instance=request.user)
        if form.is_valid():
            user = form.save(commit=False)
            # Procesar imagen recortada si viene del cropper (base64)
            cropped_data = request.POST.get("foto_perfil_cropped", "")
            if cropped_data.startswith("data:image/"):
                import base64
                from io import BytesIO

                from PIL import Image

                from django.core.files.base import ContentFile

                ALLOWED_IMG_EXTS = {"png", "jpg", "jpeg", "gif", "webp"}
                fmt, data = cropped_data.split(";base64,", 1)
                ext = fmt.split("/")[-1].split(";")[0].lower()
                if ext == "jpeg":
                    ext = "jpg"
                if ext not in ALLOWED_IMG_EXTS:
                    messages.error(request, "Formato de imagem não permitido.")
                    return redirect(_get_dashboard_url(request.user))
                image_data = base64.b64decode(data)
                if len(image_data) > 5 * 1024 * 1024:  # 5 MB máx
                    messages.error(request, "Imagem muito grande (máx. 5 MB).")
                    return redirect(_get_dashboard_url(request.user))
                try:
                    img = Image.open(BytesIO(image_data))
                    img.verify()
                except Exception:
                    messages.error(request, "Ficheiro de imagem inválido.")
                    return redirect(_get_dashboard_url(request.user))
                file_name = f"perfil_{user.pk}.{ext}"
                user.foto_perfil.save(file_name, ContentFile(image_data), save=False)
            user.save()
            messages.success(request, "Perfil atualizado com sucesso.")
            return redirect(_get_dashboard_url(request.user))
    else:
        form = EditarPerfilForm(instance=request.user)
    return render(request, "dashboard/editar_perfil.html", {"form": form})


# ============================================================
# DASHBOARDS POR ROL
# ============================================================
@paciente_required
def panel_paciente_view(request):
    """Panel del paciente: citas, historial, perfil, valoraciones."""
    from apps.appointments.models import Cita
    from apps.finance.models import Invoice
    from apps.ratings.models import Valoracion
    from django.utils import timezone

    citas_proximas = Cita.objects.filter(
        paciente=request.user,
        estado__in=["pendiente", "confirmada"],
        fecha_hora__gte=timezone.now(),
        is_active=True,
    ).count()

    citas_completadas = Cita.objects.filter(
        paciente=request.user,
        estado="completada",
        is_active=True,
    ).count()

    mis_valoraciones = Valoracion.objects.filter(
        paciente=request.user,
        is_active=True,
    ).count()

    # Citas completadas que aún no tienen valoración
    citas_valorables = (
        Cita.objects.filter(
            paciente=request.user,
            estado="completada",
            is_active=True,
        )
        .exclude(valoracion__isnull=False)
        .select_related("servicio")
        .order_by("-fecha_hora")[:5]
    )

    # Facturas del paciente
    mis_facturas = (
        Invoice.objects.filter(
            paciente=request.user,
            is_active=True,
        )
        .select_related("servicio")
        .order_by("-fecha_emision")[:10]
    )

    context = {
        "citas_proximas": citas_proximas,
        "citas_completadas": citas_completadas,
        "mis_valoraciones": mis_valoraciones,
        "citas_valorables": citas_valorables,
        "mis_facturas": mis_facturas,
    }
    return render(request, "dashboard/paciente.html", context)


@doctora_required
def panel_doctora_view(request):
    """Panel de la doctora: agenda, citas, horarios, valoraciones, finanzas."""
    from decimal import Decimal

    from apps.appointments.models import Cita
    from apps.finance.models import FacturaDia
    from apps.ratings.models import Valoracion
    from django.db.models import Sum
    from django.utils import timezone

    hoy = timezone.now().date()
    citas_hoy = Cita.objects.filter(
        fecha_hora__date=hoy,
        estado__in=["pendiente", "confirmada"],
        is_active=True,
    ).count()

    citas_pendientes = Cita.objects.filter(
        estado="pendiente",
        is_active=True,
    ).count()

    valoraciones_pendientes = Valoracion.objects.filter(
        estado=Valoracion.Estado.PENDIENTE,
        is_active=True,
    ).count()

    # Ingresos del día (FacturaDia)
    ingresos_hoy = (
        FacturaDia.objects.filter(
            fecha=hoy,
            is_active=True,
        )
        .exclude(estado=FacturaDia.Estado.ANULADA)
        .aggregate(total=Sum("precio_venta_total"))["total"]
        or Decimal("0.00")
    )

    context = {
        "citas_hoy": citas_hoy,
        "citas_pendientes": citas_pendientes,
        "valoraciones_pendientes": valoraciones_pendientes,
        "ingresos_hoy": ingresos_hoy,
    }
    return render(request, "dashboard/doctora.html", context)


@admin_required
def panel_admin_view(request):
    """Panel admin: usuarios, finanzas, reportes, configuración."""
    context = {
        "total_usuarios": User.objects.count(),
        "total_pacientes": User.objects.filter(rol="paciente").count(),
        "total_verificados": User.objects.filter(is_verified=True).count(),
    }
    return render(request, "dashboard/admin.html", context)


# ============================================================
# HELPERS INTERNOS
# ============================================================
def _get_dashboard_url(user):
    """Retorna la URL del panel según el rol del usuario."""
    dashboard_map = {
        "paciente": "users:panel_paciente",
        "doctora": "users:panel_doctora",
        "admin": "users:panel_admin",
    }
    url_name = dashboard_map.get(user.rol, "home")
    return reverse(url_name)


def _send_confirmation_email(request, user):
    """Envía email de confirmación de registro."""
    uid = urlsafe_base64_encode(force_bytes(user.pk))
    token = email_confirmation_token.make_token(user)
    path = reverse("users:confirmar_email", kwargs={"uidb64": uid, "token": token})
    confirm_url = f"{settings.SITE_URL.rstrip('/')}{path}"
    subject = "Confirme a sua conta — Dra. Mariela Moreno Autie"
    html_message = render_to_string("email/confirmacion_registro.html", {
        "user": user,
        "confirm_url": confirm_url,
    })
    plain_message = (
        f"Olá {user.nombre},\n\n"
        f"Confirme a sua conta clicando no seguinte link:\n{confirm_url}\n\n"
        f"Se não criou esta conta, ignore esta mensagem."
    )
    from django.core.mail import send_mail as _send_mail

    _send_mail(
        subject,
        plain_message,
        settings.DEFAULT_FROM_EMAIL,
        [user.email],
        html_message=html_message,
        fail_silently=False,
    )
