from typing import List, Optional
from fastapi import Body, FastAPI, Request, Response, Query, HTTPException, UploadFile, File, Cookie, WebSocket, WebSocketDisconnect, Depends, Form
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
import logging

from formulario import inscripcion
from formulario import inscripcion_wp
from comunicacion_n8n import chat_ia_n8n
from boton_descargas import boton_descargas
from voluntarios import voluntarios
from propuestas import propuestas
from comunidad import noticias
from comunidad import redes
from encuesta import encuesta

import json
from urllib.request import urlopen


from clases_landing import VerificarExistenciaResponse, VerificarExistenciaError, InscribirResponse, InscribirError, InscribirRequest, ChatN8nResponse, ChatN8nError, ChatN8nRequest, BotonDescargaRequest, BotonDescargaResponse, BotonDescargaError, VoluntariosResponse, ErrorResponse, TotalDescargasResponse, TotalDescargasErrorResponse, PropuestaRequest, NoticiasResponse, PropuestasResponse, insertarInscripcionWpRequest, InsertarRespuestasEncuestaRequest, InsertarRespuestasEncuestaResponse, InsertarRespuestasEncuestaError, ObtenerPreguntasConRespuestasResponse, ObtenerPreguntasConRespuestasError, RespuestaEncuesta


# Crear la instancia de la aplicación FastAPI
app = FastAPI(debug=True)

# Configurar CORS para permitir solicitudes desde los orígenes necesarios
app.add_middleware(
    CORSMiddleware,
    allow_origins=[
        'http://localhost:3000',
        'http://localhost:4200',
        'https://imparables.com.co',
        "https://dev.imparables.cic-ware.com",
        'https://pru.imparables.com.co',
        'https://dev.imparables.com.co'
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)

# Configurar logging para capturar y mostrar errores
logging.basicConfig(level=logging.INFO)




""" ======================= Peticiones GET ======================= """
@app.get("/ask/verificarExistencia", response_model=VerificarExistenciaResponse, responses={400: {"model": VerificarExistenciaError}, 500: {"model": VerificarExistenciaError}}, summary="Verifica si un correo o documento ya está inscrito", tags=["Inscripción"])
async def verificarExistencia(correo: str = Query(...), documento: str = Query(...)):
    try:
        resultado = inscripcion.verificar_existencia(correo, documento)
        if not resultado["success"]:
            raise HTTPException(status_code=400, detail=resultado["message"])
        return resultado
    except HTTPException:
        raise  # Re-lanza HTTPExceptions tal cual
    except Exception as e:
        logging.error(f"[ERROR] en verificar_existencia endpoint: {str(e)}")
        raise HTTPException(status_code=500, detail="Error interno al verificar existencia")



@app.get("/ask/obtenerVoluntarios", response_model=VoluntariosResponse, responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}}, summary="Obtiene el listado de voluntarios con paginación y filtro opcional por fecha", tags=["Voluntarios"])
async def obtener_voluntarios_endpoint(fecha: Optional[str] = Query(None, description="Fecha en formato YYYY-MM-DD"), pagina: int = Query(1, ge=1), por_pagina: int = Query(10, ge=1, le=100)):
    try:
        # Se invoca la función de acción que obtiene los voluntarios
        resultado = voluntarios.obtener_voluntarios(fecha, pagina, por_pagina)
        return VoluntariosResponse(
            success=1,
            data=resultado["datos"],  # Aquí se incluirán los datos de los voluntarios con los nuevos campos
            paginacion=resultado["paginacion"]
        )
    except ValueError as ve:
        raise HTTPException(status_code=400, detail=str(ve))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error al obtener voluntarios: {str(e)}")



@app.get("/ask/obtenerTodosVoluntarios", response_model=VoluntariosResponse, responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}}, summary="Obtiene todos los voluntarios sin paginación", tags=["Voluntarios"])
async def obtener_todos_voluntarios_endpoint():
    try:
        resultado = voluntarios.obtener_todos_voluntarios()
        if resultado["success"] != 1:
            raise HTTPException(status_code=400, detail=resultado.get("message", "Error desconocido"))
        return resultado  # Devuelve la respuesta tal cual
    except HTTPException:
        raise  # Re-lanza HTTPExceptions tal cual
    except Exception as e:
        logging.error(f"[ERROR] en obtenerTodosVoluntarios endpoint: {str(e)}")
        raise HTTPException(status_code=500, detail="Error interno al obtener  oluntarios")



@app.get("/ask/totalDescargasGlobal", response_model=TotalDescargasResponse, responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}}, summary="Obtiene el total general de descargas", tags=["Descargas"])
async def total_descargas_global():
    try:
        resultado = boton_descargas.obtener_total_general_descargas()
        if not resultado["success"]:
            raise HTTPException(status_code=400, detail=resultado.get("message", "Error desconocido"))
        return TotalDescargasResponse(
            success=True,
            total=resultado["total"]
        )
    except HTTPException:
        raise
    except Exception as e:
        logging.error(f"[ERROR] en total_descargas_global endpoint: {str(e)}")
        raise HTTPException(status_code=500, detail="Error interno al obtener el total de descargas")



@app.get("/ask/obtenerNoticias",response_model=NoticiasResponse,responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}},summary="Obtiene el listado de noticias con filtro opcional por fecha",tags=["Noticias"])
async def obtener_noticias_endpoint(fecha_noticia: Optional[str] = Query(None, description="Fecha en formato YYYY-MM-DD")):
    try:
        resultado = noticias.obtener_noticias(fecha_noticia)
        return {
            "success": 1,
            "data": resultado["datos"]
        }
    except ValueError as ve:
        raise HTTPException(status_code=400, detail=str(ve))
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Error al obtener noticias: {str(e)}")




@app.get("/ask/obtenerPropuestas", response_model=PropuestasResponse, responses={400: {"model": ErrorResponse}, 500: {"model": ErrorResponse}}, summary="Obtiene todos los voluntarios sin paginación", tags=["Voluntarios"])
async def obtener_todas_propuestas():
    try:
        resultado = propuestas.obtener_todas_propuestas()
        if resultado["success"] != 1:
            raise HTTPException(status_code=400, detail=resultado.get("message", "Error desconocido"))
        return resultado  # Devuelve la respuesta tal cual
    except HTTPException:
        raise  # Re-lanza HTTPExceptions tal cual
    except Exception as e:
        logging.error(f"[ERROR] en obtenerTodosVoluntarios endpoint: {str(e)}")
        raise HTTPException(status_code=500, detail="Error interno al obtener propuestas")




@app.get("/ask/obtenerTweets", summary="Obtiene los últimos tweets de un usuario", tags=["Twitter"])
async def obtener_tweets_endpoint(user_id: str = Query(..., description="ID del usuario de Twitter"), 
                                max_results: int = Query(5, ge=1, le=10, description="Número máximo de tweets")):
    try:
        resultado = redes.obtener_tweets(user_id, max_results)
        return {
            "success": 1,
            "data": resultado["datos"],
            "includes": resultado.get("includes")
        }
    except Exception as e:
        logging.error(f"[ERROR] en obtener_tweets endpoint: {str(e)}")
        raise HTTPException(status_code=500, detail=str(e))



@app.get("/ask/obtenerPreguntasConRespuestas", response_model=ObtenerPreguntasConRespuestasResponse, responses={400: {"model": ObtenerPreguntasConRespuestasError}, 500: {"model": ObtenerPreguntasConRespuestasError}}, summary="Obtiene el listado de preguntas con sus respuestas opcionales", tags=["Encuestas"])
async def obtener_preguntas_con_respuestas_endpoint(id_enc: Optional[int] = Query(None, description="ID de la encuesta específica")):
    try:
        resultado = encuesta.obtener_preguntas_con_respuestas(id_enc)
        
        if not resultado["success"]:
            raise HTTPException(status_code=400, detail=resultado["message"])
        
        return ObtenerPreguntasConRespuestasResponse(**resultado)
    except HTTPException:
        raise  # Re-lanza HTTPExceptions tal cual
    except Exception as e:
        logging.error(f"[ERROR] en obtener_preguntas_con_respuestas endpoint: {str(e)}")
        raise HTTPException(status_code=500, detail="Error interno al obtener preguntas con respuestas")

@app.get("/ask/consultarEncuestas" , summary="Obtiene el listado de encuestas disponibles", tags=["Encuestas"])
async def consultar_encuestas():
    try:
        return encuesta.traer_lista_encuestas()
    except Exception as e:
        logging.error(f"Error al consultar Encuestas: {e}")
        raise HTTPException(status_code=500, detail="Error al procesar la solicitud de consultar encuestas")

""" ======================= Peticiones POST ======================= """
@app.post("/ask/inscribir", response_model=InscribirResponse, responses={400: {"model": InscribirError}, 500: {"model": InscribirError}}, tags=["Inscripción"])
async def inscribir_voluntario(datos: InscribirRequest = Body(...)):
    try:
        resultado = inscripcion.procesar_inscripcion(datos.dict())
        return resultado
    except HTTPException as e:
        # Re-lanza exactamente el mismo error (ej. 400 personalizado)
        raise e
    except Exception as e:
        logging.exception("Error interno al procesar inscripción")
        raise HTTPException(status_code=500, detail="Error interno al procesar inscripción")



@app.post("/ask/chat-n8n", response_model=ChatN8nResponse, responses={200: {"model": ChatN8nResponse}, 500: {"model": ChatN8nError}}, tags=["Chat IA"])
async def enviar_mensaje_chat_n8n(datos: ChatN8nRequest = Body(...)):
    resultado = chat_ia_n8n.enviar_chat_n8n(datos.dict())
    return resultado



@app.post("/ask/BotonDescarga", response_model=BotonDescargaResponse, responses={200: {"model": BotonDescargaResponse}, 500: {"model": BotonDescargaError}}, tags=["Descargas"])
async def registrar_boton_descarga(datos: BotonDescargaRequest = Body(...)):
    resultado = boton_descargas.registrar_descarga(datos.dict())
    return resultado



@app.post("/ask/insertarPropuesta", response_model=dict, responses={200: {"model": dict}}, tags=["Agregar nueva propuesta"])
async def insertar_propuesta(
    nombreCompleto: str = Form(...),
    email: str = Form(...),
    tipoDocumento: str = Form(...),
    documento: str = Form(...),
    telefono: str = Form(...),
    ciudad: str = Form(...),
    tituloPropuesta: str = Form(...),
    ambitoPropuesta: str = Form(...),
    descripcionPropuesta: str = Form(...),
    adjunto: Optional[List[UploadFile]] = File(None)
):
    try:
        # Los datos del formulario ya están disponibles como variables individuales
        data = {
            'nombreCompleto': nombreCompleto,
            'email': email,
            'tipoDocumento': tipoDocumento,
            'documento': documento,
            'telefono': telefono,
            'ciudad': ciudad,
            'tituloPropuesta': tituloPropuesta,
            'ambitoPropuesta': ambitoPropuesta,
            'descripcionPropuesta': descripcionPropuesta,
            'adjunto': adjunto
        }
        
        # Ahora puedes llamar a la función que maneja la inserción
        resultado = await propuestas.propuesta_nueva(data)
        return resultado

    except HTTPException as e:
        raise e
    except Exception as e:
        logging.error(f"Error al agregar una nueva propuesta: {e}")
        raise HTTPException(status_code=500, detail="Error interno al agregar una nueva propuesta")



@app.post("/ask/insertarInscripcionesWP", response_model=dict, responses={200: {"model": dict}}, tags=["Agregar nueva inscripción WP"])
async def func(request: insertarInscripcionWpRequest):
    try:
        data = request.dict()
        resultado = inscripcion_wp.insertar_incripcion_nuevo_wp(data)
        return resultado
    except Exception as e:
        logging.error(f"Error al agregar una nueva inscripción de contacto de WhatsApp: {e}")
        raise HTTPException(status_code=500, detail="Error al procesar la solicitud de inscripción de contacto de WhatsApp")



@app.post("/ask/insertarRespuestasEncuesta", response_model=InsertarRespuestasEncuestaResponse, responses={400: {"model": InsertarRespuestasEncuestaError}, 500: {"model": InsertarRespuestasEncuestaError}}, summary="Inserta las respuestas del usuario en la encuesta", tags=["Encuestas"])
async def insertar_respuestas_encuesta_endpoint(respuestas: List[RespuestaEncuesta] = Body(...)):
    try:
        # Convertir las respuestas a formato dict para la función
        respuestas_dict = [respuesta.dict() for respuesta in respuestas]
        resultado = encuesta.insertar_respuestas_encuesta(respuestas_dict)
        
        if not resultado["success"]:
            raise HTTPException(status_code=400, detail=resultado["message"])
        
        return InsertarRespuestasEncuestaResponse(**resultado)
    except HTTPException:
        raise  # Re-lanza HTTPExceptions tal cual
    except Exception as e:
        logging.error(f"[ERROR] en insertar_respuestas_encuesta endpoint: {str(e)}")
        raise HTTPException(status_code=500, detail="Error interno al insertar respuestas de encuesta")


if __name__ == "__main__":
    import uvicorn
    uvicorn.run(app, host="0.0.0.0", port=5010)