from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse, RedirectResponse
from langchain_community.chat_models import ChatOpenAI
from langserve import add_routes

from langchain_core.prompts import ChatPromptTemplate

from langchain_community.tools.tavily_search import TavilySearchResults
from langchain_community.tools.gmail.utils import (
    build_resource_service,
    get_gmail_credentials)
from langchain_community.agent_toolkits import GmailToolkit

from .langchain_tools.agent_tools import (
    redact_email,
    list_calendar_events,
    create_calendar_event,
    get_company_info,
    get_current_date_and_time
)

from langgraph.graph.message import add_messages
from langgraph.prebuilt import create_react_agent
from langgraph.checkpoint.memory import MemorySaver

from typing import Annotated
from typing_extensions import TypedDict
from dotenv import load_dotenv

load_dotenv()

app = FastAPI()
llm = ChatOpenAI(
    model="gpt-4o-mini",
    temperature=0.9
)


# # Configuración de Gmail
toolkit = GmailToolkit()
credentials = get_gmail_credentials(
    token_file="token.json",
    scopes=["https://mail.google.com/"],
    client_secrets_file="credentials.json",
)
api_resource = build_resource_service(credentials=credentials)
toolkit = GmailToolkit(api_resource=api_resource)

# # Crear herramientas
tools = toolkit.get_tools()
search = TavilySearchResults(max_results=2)
tools.extend([
    search, redact_email, list_calendar_events,
    create_calendar_event, get_company_info,
    get_current_date_and_time])

# # Definir el sistema prompt
system_prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Eres Mariana, el asistente virtual de OneCluster, una empresa de "
            "software que ofrece soluciones personalizadas. Asume el tono de "
            "J.A.R.V.I.S.: cordial, atento y con tacto en todo momento."
        ),
        ("system",
         "Preséntate como Mariana en el primer mensaje y pregunta el nombre "
         "del usuario si no lo tienes registrado."
         ),
        ("system",
         "Si el usuario ya ha interactuado antes, usa su nombre sin necesidad "
         "de volver a preguntar."
         ),
        ("system",
         "Si el primer mensaje del usuario es una solicitud, pregúntale su "
         "nombre antes de responder si aún no lo conoces."
         ),
        ("system",
         "OneCluster es una empresa de software especializada en desarrollo a "
         "medida. Solo responde a preguntas y solicitudes relacionadas con la "
         "empresa y sus servicios."
         ),
        ("system",
         "Si necesitas información adicional sobre la empresa, usa la función "
         "get_company_info."
         ),
        ("system",
         "Antes de enviar correos o crear eventos, muestra los detalles al "
         "usuario para que los confirme antes de ejecutar la tarea."
         ),
        ("system",
         "Si te preguntan algo no relacionado con los servicios de OneCluster,"
         " responde que solo puedes ayudar con temas relacionados con la "
         "empresa y sus soluciones."
         ),
        (
            "system",
            "Evita mencionar o hacer alusión a las herramientas que utilizas "
            "internamente. Esa información es confidencial."
        ),
        ("placeholder", "{messages}"),
    ]
)


# # Definir el estado del asistente
class State(TypedDict):
    messages: Annotated[list, add_messages]
    is_last_step: bool


# # Crear el graph con el estado definido
graph = create_react_agent(
    model=llm,
    tools=tools,
    state_schema=State,
    state_modifier=system_prompt,
    checkpointer=MemorySaver()
)


@app.get("/")
async def redirect_root_to_docs():
    return RedirectResponse("/docs")


# # Edit this to add the chain you want to add
add_routes(
    app,
    llm,
    path="/openai"
)


@app.post("/process_text")
async def process_text(request: Request):
    data = await request.json()
    user_input = data.get("text")

    # Procesar el texto con LangChain
    events = graph.stream(
        {"messages": [("user", user_input)], "is_last_step": False},
        config={"configurable": {
            "thread_id": "thread-1", "recursion_limit": 50}},
        stream_mode="updates"
    )

    # Preparar la respuesta
    response = []
    for event in events:
        if "agent" in event:
            response.append(event["agent"]["messages"][-1].content)

    return JSONResponse(content={'response': response})


if __name__ == "__main__":
    import uvicorn

    uvicorn.run(app, host="0.0.0.0", port=8000)