from django.shortcuts import render, redirect
from django.http import JsonResponse
from django.conf import settings
from django.views.decorators.csrf import csrf_exempt
import os
import json

from .models import DiccionarioTabla, DiccionarioCampo, DiccionarioRelacion

from login.models import Usuario

def datahub(request):
    if 'user_id' not in request.session:
        return redirect('/')

    return render(request, 'datahub.html')


@csrf_exempt
def get_tables_json(request):
    """Devuelve todas las tablas con sus campos y relaciones en JSON."""
    # Prefetch de campos y relaciones
    qs = DiccionarioTabla.objects.prefetch_related(
        'campos',
        'campos__relaciones_origen'
    ).all()

    data = []

    for t in qs:
        campos = []
        
        # APLICAR ORDER BY ID para asegurar consistencia en el frontend
        for c in t.campos.all().order_by('id'):
            relaciones = []
            for rel in c.relaciones_origen.all():
                relaciones.append({
                    'tabla_destino': rel.tabla_destino,
                    'campo_destino': rel.campo_destino,
                    'descripcion': rel.descripcion,
                    'nombres_exactos': None, # se completa más abajo
                })

            campos.append({
                'id': c.id,
                'nombre': c.nombre,
                'descripcion': c.descripcion,
                'tipo_dato': c.tipo_dato,
                'relaciones': relaciones,
            })

        data.append({
            'tabla': {
                'id': t.id,
                'nombre_sap': getattr(t, 'nombre_sap', ''),
                'nombre': t.nombre,
                'descripcion': t.descripcion,
                'tipo': t.tipo,
                'origen': t.origen,
                'actualizacion': t.actualizacion,
                'area': t.area,
            },
            'campos': campos,
        })

    # Resolver en lote las tablas destino mencionadas
    destino_nombres = set()
    for item in data:
        for c in item.get('campos', []):
            for r in c.get('relaciones', []):
                td = r.get('tabla_destino')
                if td:
                    destino_nombres.add(td)

    tablas_destino = {}
    if destino_nombres:
        tqs = DiccionarioTabla.objects.filter(nombre__in=destino_nombres).prefetch_related('campos')
        for td in tqs:
            tablas_destino[td.nombre] = {
                'id': td.id,
                'nombre_sap': getattr(td, 'nombre_sap', ''),
                'nombre': td.nombre,
                'descripcion': td.descripcion,
                'tipo': td.tipo,
                'origen': td.origen,
                'actualizacion': td.actualizacion,
                'area': td.area,
                'campos': [
                    {
                        'id': c.id,
                        'nombre': c.nombre,
                        'descripcion': c.descripcion,
                        'tipo_dato': c.tipo_dato,
                    }
                    # Ordenar también los campos de tablas destino
                    for c in td.campos.all().order_by('id') 
                ]
            }

    # Enriquecer relaciones con las tablas destino
    for item in data:
        for c in item.get('campos', []):
            for r in c.get('relaciones', []):
                td = r.get('tabla_destino')
                if isinstance(td, str) and td in tablas_destino:
                    r['nombres_exactos'] = {
                        'nombre': tablas_destino[td]['nombre'],
                        'campo_destino': r.get('campo_destino') or ''
                    }
                else:
                    r['nombres_exactos'] = {
                        'nombre': td if td else '',
                        'campo_destino': r.get('campo_destino') or ''
                    }
    return JsonResponse({'success': True, 'data': data}, json_dumps_params={'ensure_ascii': False})

from django.views.decorators.csrf import csrf_exempt
from django.http import JsonResponse
from django.db import transaction
import json

from .models import DiccionarioTabla, DiccionarioCampo, DiccionarioRelacion

@csrf_exempt
def create_table_from_json(request):
    try:
        json_data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({'success': False, 'error': 'JSON inválido'}, status=400)

    tabla_ids = {}
    campo_ids = {}

    try:
        with transaction.atomic():
            # 1️⃣ Crear o actualizar tablas
            for t in json_data.get('tablas', []):
                nombre_tabla = (t.get('nombre') or '').strip()
                if not nombre_tabla:
                    continue

                tabla_obj, _ = DiccionarioTabla.objects.update_or_create(
                    nombre=nombre_tabla,
                    defaults={
                        'nombre_sap': t.get('nombre_sap', ''),
                        'descripcion': t.get('descripcion', ''),
                        'tipo': t.get('tipo', ''),
                        'origen': t.get('origen', ''),
                        'actualizacion': t.get('actualizacion', ''),
                        'area': t.get('area', ''),
                    }
                )
                tabla_ids[nombre_tabla] = tabla_obj.id

                # 2️⃣ Crear o actualizar campos de la tabla
                for c in t.get('campos', []):
                    c_name = (c.get('nombre') or '').strip()
                    if not c_name:
                        continue

                    campo_obj, _ = DiccionarioCampo.objects.update_or_create(
                        tabla=tabla_obj,
                        nombre=c_name,
                        defaults={
                            'descripcion': c.get('descripcion', ''),
                            'tipo_dato': c.get('tipo_dato', ''),
                        }
                    )
                    campo_ids[f"{nombre_tabla}.{c_name}"] = campo_obj.id

            # 3️⃣ Crear relaciones
            for t in json_data.get('tablas', []):
                tname = (t.get('nombre') or '').strip()
                if not tname:
                    continue
                for c in t.get('campos', []):
                    cname = (c.get('nombre') or '').strip()
                    if not cname:
                        continue

                    campo_origen_id = campo_ids.get(f"{tname}.{cname}")
                    if not campo_origen_id:
                        continue

                    for r in c.get('relaciones', []):
                        if isinstance(r, dict):
                            r.pop('id', None)
                            r.pop('pk', None)

                        tabla_destino = (r.get('tabla_destino') or '').strip()
                        campo_destino = (r.get('campo_destino') or '').strip()
                        descripcion = (r.get('descripcion') or '').strip()

                        # Crear relación sólo si no existe igual
                        DiccionarioRelacion.objects.get_or_create(
                            campo_origen_id=campo_origen_id,
                            tabla_destino=tabla_destino,
                            campo_destino=campo_destino,
                            defaults={'descripcion': descripcion}
                        )

        return JsonResponse({'success': True, 'message': 'Datos guardados correctamente'})

    except Exception as e:
        return JsonResponse({'success': False, 'error': str(e)}, status=500)

from django.http import JsonResponse, HttpResponse, HttpResponseNotFound
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from .models import DiccionarioCampo # Asume que models.py está en el mismo directorio
import json # Necesario para parsear el cuerpo de la solicitud PATCH

# Se requiere @csrf_exempt para API DELETE y PATCH si no se usa un middleware o librería REST,
# aunque se recomienda verificar el token X-CSRFToken en producción.

@csrf_exempt
@require_http_methods(["POST"])
def crear_campo_api(request, tabla_id):
    """
    Crea un nuevo campo (DiccionarioCampo) asociado a una tabla (DiccionarioTabla).
    Recibe: {nombre: str, descripcion: str, tipo_dato: str}
    """
    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({'error': 'JSON inválido'}, status=400)
    
    try:
        tabla = DiccionarioTabla.objects.get(pk=tabla_id)
    except DiccionarioTabla.DoesNotExist:
        return HttpResponseNotFound(f"Tabla con ID {tabla_id} no encontrada.")

    # Validar campos esenciales
    nombre = data.get('nombre', '').strip()
    if not nombre:
        return JsonResponse({'error': 'El nombre del campo es obligatorio.'}, status=400)
    
    try:
        with transaction.atomic():
            nuevo_campo = DiccionarioCampo.objects.create(
                tabla=tabla,
                nombre=nombre,
                descripcion=data.get('descripcion', ''),
                tipo_dato=data.get('tipo_dato', ''),
            )
        
        return JsonResponse({
            'success': True, 
            'message': f'Campo "{nombre}" creado exitosamente.',
            'id': nuevo_campo.id,
            'nombre': nuevo_campo.nombre,
        }, status=201) # 201 Created

    except Exception as e:
        return JsonResponse({'error': str(e)}, status=500)


@csrf_exempt
@require_http_methods(["DELETE"])
def eliminar_campo_api(request, campo_id):
    """
    Elimina un campo de DiccionarioCampo dado su ID.
    
    Args:
        request: Objeto HttpRequest.
        campo_id: ID (PK) del DiccionarioCampo a eliminar.
    """
    try:
        campo = DiccionarioCampo.objects.get(pk=campo_id)
    except DiccionarioCampo.DoesNotExist:
        # Si el campo no existe, devolvemos un 404
        return HttpResponseNotFound(f"Campo con ID {campo_id} no encontrado.")
    except Exception as e:
        # Manejo de errores de base de datos
        return JsonResponse({'error': str(e)}, status=500)

    try:
        campo.delete()
        # Devolver un 204 No Content para indicar éxito sin cuerpo de respuesta
        return HttpResponse(status=204)
    except Exception as e:
        # Si falla la eliminación por alguna razón (e.g., restricciones de FK),
        # se devuelve un error 500.
        print(f"Error al eliminar campo {campo_id}: {e}")
        return JsonResponse({'error': 'No se pudo eliminar el campo debido a un error interno.'}, status=500)


@csrf_exempt
@require_http_methods(["PATCH"])
def actualizar_campo_api(request, campo_id):
    """
    Actualiza el nombre y/o la descripción de un DiccionarioCampo.
    
    Args:
        request: Objeto HttpRequest (contiene JSON con 'nombre' y/o 'descripcion').
        campo_id: ID (PK) del DiccionarioCampo a actualizar.
    """
    try:
        data = json.loads(request.body)
    except json.JSONDecodeError:
        return JsonResponse({'error': 'JSON inválido'}, status=400)

    try:
        campo = DiccionarioCampo.objects.get(pk=campo_id)
    except DiccionarioCampo.DoesNotExist:
        return HttpResponseNotFound(f"Campo con ID {campo_id} no encontrado.")
    
    cambios_realizados = False
    
    # Solo actualiza si el campo está presente en el JSON
    if 'nombre' in data:
        campo.nombre = data['nombre']
        cambios_realizados = True

    if 'descripcion' in data:
        campo.descripcion = data['descripcion']
        cambios_realizados = True
    
    # El tipo_dato también es editable en el front, así que lo incluimos aquí.
    if 'tipo_dato' in data:
        campo.tipo_dato = data['tipo_dato']
        cambios_realizados = True

    if cambios_realizados:
        try:
            campo.save()
            return JsonResponse({'success': True, 'message': 'Campo actualizado correctamente.'}, status=200)
        except Exception as e:
            # Error de integridad (e.g., nombre duplicado si hubiera restricción unique)
            return JsonResponse({'error': f'Error al guardar en BD: {e}'}, status=500)
    else:
        # Si la solicitud no contenía datos de actualización, se considera un éxito (no se hizo nada)
        return JsonResponse({'success': True, 'message': 'No se proporcionaron campos válidos para actualizar.'}, status=200)
