from django.shortcuts import render, redirect
from django.http import JsonResponse, HttpResponse
from django.conf import settings
from django.db.models import Prefetch
from django.views.decorators.http import require_http_methods
from django.db import IntegrityError, transaction 
import json
import os

from login.models import Usuario, Area, Gerencia
from usuarios.models import PermisoArea, PermisoUrl, Solicitud, Url

def usuarios_admin_view(request):
    if 'user_id' not in request.session or request.session.get('user_tipo') != 2:
        # Redirigir al login si no hay sesión
        return redirect('/')
    
    return render(request, 'usuarios_admin.html')

def get_usuarios_admin(request):
    """
    Retorna una lista de usuarios con sus permisos, MÁS las listas maestras de 
    Áreas (incluyendo Gerencia) y URLs. Optimizada con Prefetch(to_attr) y list comprehensions.
    """
    if 'user_id' not in request.session:
        return JsonResponse({'error': 'Unauthorized'}, status=401)
        
    try:
        current_user_id = request.session['user_id']
        try:
            current_user = Usuario.objects.select_related('area').get(id=current_user_id)
        except Usuario.DoesNotExist:
            return JsonResponse({'error': 'User not found'}, status=404)

        # --- Determine administrable areas ---
        is_superadmin = current_user.tipo == 1
        admin_area_ids = []

        if is_superadmin:
            # Superadmin can see all users from all areas
            usuarios_query = Usuario.objects.all()
        else:
            admin_area_ids_set = set()
            # Area admin (tipo=2) can see their own area.
            if current_user.tipo == 2 and current_user.area:
                admin_area_ids_set.add(current_user.area.id)
            
            # Add areas from PermisoUrl
            urls_con_permiso = PermisoUrl.objects.filter(usuario=current_user, estado=1).select_related('url__area').values_list('url__area_id', flat=True)
            admin_area_ids_set.update(urls_con_permiso)
            admin_area_ids_set.discard(None)
            
            admin_area_ids = list(admin_area_ids_set)
            # Filter users belonging to the administrable areas
            usuarios_query = Usuario.objects.filter(area_id__in=admin_area_ids)

        # --- 1. Definición de Prefetching (Optimizando N+1 Queries) ---
        
        # Prefetch para PermisoArea: SOLO TRAE PERMISOS ACTIVOS
        permiso_area_prefetch = Prefetch(
            'permisos_area',
            queryset=PermisoArea.objects.filter(estado=1).select_related('area'),
            to_attr='_permisos_area'
        )
        
        # Prefetch para PermisoUrl: SOLO TRAE PERMISOS ACTIVOS
        permiso_url_prefetch = Prefetch(
            'permisos_url', 
            queryset=PermisoUrl.objects.filter(estado=1).select_related('url'),
            to_attr='_permisos_url'
        )
        
        # Prefetch para Solicitudes
        solicitudes_prefetch = Prefetch(
            'solicitudes', 
            queryset=Solicitud.objects.select_related('solicitud_url', 'solicitud_area'),
            to_attr='_solicitudes'
        )

        # 2. Obtener usuarios (select_related para Area y Gerencia principal)
        usuarios = usuarios_query.select_related('area', 'area__gerencia').prefetch_related(
            permiso_area_prefetch,
            permiso_url_prefetch,
            solicitudes_prefetch
        ).all().order_by('nombre')
        
        # --- 3. Obtener Listas Maestras ---
        
        # A. Áreas Maestras - Filter based on admin's areas if not superadmin
        if is_superadmin:
            areas_maestras = Area.objects.select_related('gerencia').filter(urls__isnull=False).distinct().order_by('nombre')
        else:
            areas_maestras = Area.objects.select_related('gerencia').filter(id__in=admin_area_ids, urls__isnull=False).distinct().order_by('nombre')

        maestro_areas_data = [{
            'id': a.id,
            'nombre': a.nombre,
            'gerencia_id': a.gerencia_id, 
            'gerencia_nombre': a.gerencia.nombre if a.gerencia else 'Sin Gerencia',
        } for a in areas_maestras]

        # B. URLs Maestras (incluimos info de área para poder agrupar en el frontend)
        if is_superadmin:
            urls_maestras = Url.objects.select_related('area', 'area__gerencia').all().order_by('nombre')
        else:
            urls_maestras = Url.objects.select_related('area', 'area__gerencia').filter(area_id__in=admin_area_ids).order_by('nombre')

        maestro_urls_data = [{
            'id': u.id,
            'nombre': u.nombre,
            'url': u.url,
            'area_id': u.area_id,
            'area_nombre': u.area.nombre if u.area else 'Sin área',
            'gerencia_id': u.area.gerencia_id if u.area and u.area.gerencia else None,
            'gerencia_nombre': u.area.gerencia.nombre if u.area and u.area.gerencia else 'Sin gerencia',
        } for u in urls_maestras]

        # --- 4. Serializar datos de usuarios individuales (Optimizada) ---
        
        usuarios_data = []
        for u in usuarios:
            # Serialización eficiente de Permisos de Área
            permisos_area_data = [{
                'area_nombre': pa.area.nombre,
                'area_id': pa.area.id,
                'estado': pa.estado,
            } for pa in u._permisos_area] 

            # Serialización eficiente de Permisos de URL
            permisos_url_data = [{
                'url_nombre': pu.url.nombre,
                'url_id': pu.url.id,
                'url': pu.url.url,
                'estado': pu.estado,
            } for pu in u._permisos_url]

            # Serialización eficiente de Solicitudes
            solicitudes_data = [{
                'id': s.id,
                'url_nombre': s.solicitud_url.nombre if s.solicitud_url else 'N/A',
                'area_nombre': s.solicitud_area.nombre if s.solicitud_area else 'N/A',
                'justificacion': s.justificacion,
                'estado_codigo': s.estado,
                'estado_display': s.get_estado_display(),
            } for s in u._solicitudes]
            
            # Serialización de datos base del usuario
            usuarios_data.append({
                'id': u.id,
                'nombre': u.nombre,
                'correo': u.correo,
                'cc': u.cc,
                'estado': u.estado,
                'tipo': u.tipo,
                'pais': u.pais,
                'ciudad': u.ciudad,
                'area_id': u.area_id, 
                'area': u.area.nombre if u.area else 'Sin área',
                # ⭐ Importante: Enviamos el ID numérico de Gerencia y el nombre
                'gerencia_id': u.area.gerencia_id if u.area and u.area.gerencia else None, 
                'gerencia': u.area.gerencia.nombre if u.area and u.area.gerencia else 'Sin gerencia',
                'img': f"{settings.MEDIA_URL}profile/{u.img}" if u.img else f"{settings.MEDIA_URL}profile/default.png",
                
                'permisos_area': permisos_area_data,
                'permisos_url': permisos_url_data,
                'solicitudes': solicitudes_data,
            })
            
        # 5. Retornar la respuesta
        return JsonResponse({
            'usuarios': usuarios_data,
            'maestro_areas': maestro_areas_data, 
            'maestro_urls': maestro_urls_data, 
        })
        
    except Exception as e:
        print(f"Error al obtener usuarios: {e}")
        return JsonResponse({'usuarios': [], 'maestro_areas': [], 'maestro_urls': [], 'error': str(e)}, status=500)

### API Views (POST)
@require_http_methods(["POST"])
@transaction.atomic
def update_usuario(request):
    """
    Actualiza el nombre y el estado de un usuario.
    """
    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({'error': 'Formato JSON inválido.'}, status=400)

    user_cc = data.get('cc')
    if not user_cc:
        return JsonResponse({'error': 'CC (cédula) del usuario es requerido.'}, status=400)

    # 1. Buscar el usuario
    try:
        usuario = Usuario.objects.get(cc=user_cc)
    except Usuario.DoesNotExist:
        return JsonResponse({'error': f'Usuario con CC {user_cc} no encontrado.'}, status=404)

    # 2. Asignar y validar campos
    fields_to_update = []
    try:
        if 'nombre' in data and data['nombre']:
            usuario.nombre = data['nombre']
            fields_to_update.append('nombre')

        if 'estado' in data and data['estado'] is not None:
            new_status = int(data['estado'])
            if new_status not in [0, 1]:
                raise ValueError("El campo estado debe ser 0 o 1.")
            usuario.estado = new_status
            fields_to_update.append('estado')
        
    except (ValueError, TypeError):
        return JsonResponse({'error': 'Error de validación en los campos.'}, status=400)

    # 3. Guardar los cambios
    if fields_to_update:
        try:
            usuario.save(update_fields=fields_to_update)
        except IntegrityError:
            # This could happen if 'nombre' is a unique field and there's a conflict.
            return JsonResponse({'error': 'Error de integridad en la base de datos (ej: nombre duplicado).'}, status=400)
        except Exception as e:
            print(f"Error al guardar el usuario en DB: {e}")
            return JsonResponse({'error': 'Error interno del servidor al guardar los cambios.'}, status=500)

    # 4. Respuesta exitosa
    return JsonResponse({'message': 'Usuario actualizado exitosamente.', 'cc': usuario.cc})


@require_http_methods(["POST"])
@transaction.atomic
def update_area_perms(request):
    """
    Recibe JSON para actualizar Permisos de Área y utiliza update_or_create 
    dentro de una transacción atómica para garantizar la consistencia.
    """
    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({'error': 'Formato JSON inválido.'}, status=400)

    user_cc = data.get('cc')
    areas = data.get('areas')
    if not user_cc or areas is None:
        return JsonResponse({'error': 'Parámetros missing: cc y areas son requeridos.'}, status=400)

    try:
        usuario = Usuario.objects.get(cc=user_cc)
    except Usuario.DoesNotExist:
        return JsonResponse({'error': f'Usuario con CC {user_cc} no encontrado.'}, status=404)

    changes = []
    url_changes = []
    with transaction.atomic():
        # Primero, obtenemos todos los permisos actuales del usuario
        permisos_actuales = PermisoArea.objects.filter(usuario=usuario)
        
        # Creamos un diccionario para búsqueda rápida
        permisos_dict = {p.area_id: p for p in permisos_actuales}
        
        # Procesamos las áreas recibidas
        for a in areas:
            try:
                area_id = int(a.get('id'))
                checked = bool(a.get('checked'))
                estado_deseado = 1 if checked else 0
            except Exception as e:
                print(f"Error procesando área: {a}, error: {e}")
                continue 

            try:
                area_obj = Area.objects.get(id=area_id)
            except Area.DoesNotExist:
                print(f"Área no encontrada: {area_id}")
                continue

            # Si el permiso existe, actualizamos su estado
            if area_id in permisos_dict:
                permiso = permisos_dict[area_id]
                if permiso.estado != estado_deseado:
                    permiso.estado = estado_deseado
                    permiso.save()
                    changes.append({'area_id': area_id, 'action': 'updated', 'estado': estado_deseado})
            else:
                # Si no existe, creamos uno nuevo
                permiso = PermisoArea.objects.create(
                    usuario=usuario,
                    area=area_obj,
                    estado=estado_deseado
                )
                changes.append({'area_id': area_id, 'action': 'created', 'estado': estado_deseado})
            
            # Si se desactiva el permiso de área, desactivar todas las URLs relacionadas
            if estado_deseado == 0:
                permisos_url_actualizados = PermisoUrl.objects.filter(
                    usuario=usuario,
                    url__area_id=area_id,
                    estado=1
                )

                urls_afectadas = list(permisos_url_actualizados.values_list('url_id', flat=True))
                if urls_afectadas:
                    permisos_url_actualizados.update(estado=0)
                    for url_id in urls_afectadas:
                        url_changes.append({
                            'url_id': url_id,
                            'area_id': area_id,
                            'action': 'updated',
                            'estado': 0
                        })

            
    # Registramos todos los cambios en la respuesta
    return JsonResponse({
        'message': 'Permisos de área actualizados.',
        'changes': changes,
        'url_changes': url_changes,
        'debug_info': {
            'areas_recibidas': areas,
            'cambios_realizados': changes,
            'urls_actualizadas': url_changes
        }
    })


@require_http_methods(["POST"])
@transaction.atomic
def update_url_perms(request):
    """
    Recibe JSON para actualizar Permisos de URL y utiliza update_or_create
    dentro de una transacción atómica para garantizar la consistencia.
    """
    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({'error': 'Formato JSON inválido.'}, status=400)

    user_cc = data.get('cc')
    urls = data.get('urls')
    if not user_cc or urls is None:
        return JsonResponse({'error': 'Parámetros missing: cc y urls son requeridos.'}, status=400)

    try:
        usuario = Usuario.objects.get(cc=user_cc)
    except Usuario.DoesNotExist:
        return JsonResponse({'error': f'Usuario con CC {user_cc} no encontrado.'}, status=404)

    changes = []
    with transaction.atomic():
        # Primero, obtener todas las URLs existentes para el usuario
        permisos_existentes = PermisoUrl.objects.filter(usuario=usuario)
        permisos_dict = {str(p.url_id): p for p in permisos_existentes}

        # Procesar las URLs recibidas
        for u in urls:
            try:
                url_id = int(u.get('id'))
                checked = bool(u.get('checked'))
                estado_deseado = 1 if checked else 0
            except Exception as e:
                print(f"Error procesando URL: {u}, error: {e}")
                continue

            try:
                url_obj = Url.objects.get(id=url_id)
            except Url.DoesNotExist:
                print(f"URL no encontrada: {url_id}")
                continue

            # Si el permiso existe, actualizar su estado
            if str(url_id) in permisos_dict:
                permiso = permisos_dict[str(url_id)]
                if permiso.estado != estado_deseado:
                    permiso.estado = estado_deseado
                    permiso.save()
                    changes.append({'url_id': url_id, 'action': 'updated', 'estado': estado_deseado})
            else:
                # Si no existe y se está activando (checked=True), crear uno nuevo
                # Si se está desactivando (checked=False) y no existe, no crear el permiso
                if checked:
                    permiso = PermisoUrl.objects.create(
                        usuario=usuario,
                        url=url_obj,
                        estado=estado_deseado
                    )
                    changes.append({'url_id': url_id, 'action': 'created', 'estado': estado_deseado})
                # Si checked=False y no existe el permiso, no hacer nada (ya está desactivado)

    return JsonResponse({
        'message': 'Permisos de URL actualizados.',
        'changes': changes,
        'debug_info': {
            'urls_recibidas': urls,
            'cambios_realizados': changes
        }
    })