from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from datetime import datetime, timezone
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from googleapiclient.discovery import build
from ..rag.split_docs import load_split_docs
from ..rag.llm import load_llm_openai
from ..rag.embeddings import load_embeddins
from ..rag.retriever import create_retriever
from ..rag.vectorstore import create_verctorstore
from ..rag.rag_chain import create_rag_chain
import pytz
import telebot
import os


class LangChainTools:
    def load_llm_openai(self):
        load_dotenv()
        # model = "gpt-3.5-turbo-0125"
        # model = "gpt-4o"
        model = "gpt-4o-mini"

        llm = ChatOpenAI(
            model=model,
            temperature=0.1,
            max_tokens=2000,
        )
        return llm


@tool
def multiply(first_int: int, second_int: int) -> int:
    """Multiply two integers together."""
    return first_int * second_int


@tool
def redact_email(topic: str) -> str:
    """Use this tool to draft the content of an email based on a topic."""

    # Load LLM model
    langChainTools = LangChainTools()

    llm = langChainTools.load_llm_openai()
    # Create prompt for the LLM
    prompt = (
        "Please redact a email based on the topic:\n\n"
        "Topic: {}\n\n"
        "Email Content: [Your email content here]"
    ).format(topic)

    response = llm.invoke(prompt)
    return response


@tool
def list_calendar_events(max_results: int = 50) -> list:
    """Use this tool to list upcoming calendar events."""

    # Define los alcances que necesitamos para acceder a
    # la API de Google Calendar
    SCOPES = ['https://www.googleapis.com/auth/calendar']

    creds = None

    # La ruta al archivo token.json, que contiene
    # los tokens de acceso y actualización
    token_path = 'token.json'

    # La ruta al archivo de credenciales de OAuth 2.1
    creds_path = 'credentials.json'

    # Cargar las credenciales desde el archivo token.json, si existe
    if os.path.exists(token_path):
        creds = Credentials.from_authorized_user_file(token_path, SCOPES)

    # Si no hay credenciales válidas disponibles, inicia el flujo de OAuth 2.0
    # para obtener nuevas credenciales
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                creds_path, SCOPES)
            creds = flow.run_local_server(port=0)

        # Guarda las credenciales para la próxima ejecución
        with open(token_path, 'w') as token_file:
            token_file.write(creds.to_json())

    # Construye el objeto de servicio para interactuar
    # con la API de Google Calendar
    service = build('calendar', 'v3', credentials=creds)

    # Identificador del calendario que deseas consultar.
    # 'primary' se refiere al calendario principal del usuario.
    calendar_id = 'primary'

    # Realiza una llamada a la API para obtener una lista de eventos.
    now = datetime.now(timezone.utc).isoformat()  # 'Z' indica UTC
    events_result = service.events().list(
        calendarId=calendar_id, timeMin=now,
        maxResults=max_results, singleEvents=True,
        orderBy='startTime').execute()

    # Extrae los eventos de la respuesta de la API.
    events = events_result.get('items', [])

    # Si no se encuentran eventos, imprime un mensaje.
    if not events:
        print('No upcoming events found.')
        return

    # Recorre la lista de eventos y muestra la hora de inicio
    # y el resumen de cada evento.
    for event in events:
        # Obtiene la fecha y hora de inicio del evento.
        # Puede ser 'dateTime' o 'date'.
        start = event['start'].get('dateTime', event['start'].get('date'))
        # Imprime la hora de inicio y el resumen (título) del evento.
        print(start, event['summary'])

    return events


@tool
def create_calendar_event(
        title: str, start_time: datetime,
        end_time: datetime, attendees: list) -> dict:
    """Use this tool to create an event in the calendar.

    Parameters:
    - title: str - The title of the event.
    - start_time: datetime - The start time of the event.
    - end_time: datetime - The end time of the event.
    - attendees: list - A list of attendee emails (required).

    Returns:
    - dict - The created event details.
    """

    if not attendees:
        raise ValueError(
            "El campo 'attendees' es obligatorio y no puede estar vacío.")

    SCOPES = ['https://www.googleapis.com/auth/calendar']
    creds = None

    # La ruta al archivo token.json,
    # que contiene los tokens de acceso y actualización
    token_path = 'token_2.json'

    # La ruta al archivo de credenciales de OAuth 2.0
    creds_path = 'credentials_2.json'

    # Cargar las credenciales desde el archivo token.json, si existe
    if os.path.exists(token_path):
        creds = Credentials.from_authorized_user_file(token_path, SCOPES)

    # Si no hay credenciales válidas disponibles,
    # inicia el flujo de OAuth 2.0 para obtener nuevas credenciales
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                creds_path, SCOPES)
            creds = flow.run_local_server(port=0)

        # Guarda las credenciales para la próxima ejecución
        with open(token_path, 'w') as token_file:
            token_file.write(creds.to_json())

    # Construye el objeto de servicio para
    # interactuar con la API de Google Calendar
    service = build('calendar', 'v3', credentials=creds)

    # Validar y filtrar asistentes
    valid_attendees = []
    for email in attendees:
        if isinstance(email, str) and '@' in email:
            valid_attendees.append({'email': email})
        else:
            raise ValueError(f"'{email}' no es un correo electrónico válido.")

    # Identificador del calendario que deseas modificar.
    # 'primary' se refiere al calendario principal del usuario.
    calendar_id = 'primary'

    # Define el cuerpo del evento con el título,
    # la hora de inicio y la hora de finalización
    event = {
        'summary': title,
        'start': {
            'dateTime': start_time.strftime('%Y-%m-%dT%H:%M:%S'),
            'timeZone': 'America/Bogota',
        },
        'end': {
            'dateTime': end_time.strftime('%Y-%m-%dT%H:%M:%S'),
            'timeZone': 'America/Bogota',
        },
        'attendees': valid_attendees
    }

    try:
        # Crea el evento en el calendario
        event = service.events().insert(
            calendarId=calendar_id, body=event).execute()
        print('Event created: %s' % (event.get('htmlLink')))
    except Exception as e:
        print(f"Error al crear el evento: {e}")
        return {}

    return event


@tool
def create_quick_add_event(quick_add_text: str):
    """
    Use this tool to create events in the calendar from natural language,
    using the Quick Add feature of Google Calendar.
    """
    quick_add_text: str = input(
        "- Escribe la descripcion del evento que quieres crear: ")
    SCOPES = ['https://www.googleapis.com/auth/calendar']

    creds = None

    # La ruta al archivo token.json,
    # que contiene los tokens de acceso y actualización
    token_path = 'token_2.json'

    # La ruta al archivo de credenciales de OAuth 2.0
    creds_path = 'credentials_2.json'

    # Cargar las credenciales desde el archivo token.json, si existe
    if os.path.exists(token_path):
        creds = Credentials.from_authorized_user_file(token_path, SCOPES)

    # Si no hay credenciales válidas disponibles,
    # inicia el flujo de OAuth 2.0 para obtener nuevas credenciales
    if not creds or not creds.valid:
        if creds and creds.expired and creds.refresh_token:
            creds.refresh(Request())
        else:
            flow = InstalledAppFlow.from_client_secrets_file(
                creds_path, SCOPES)
            creds = flow.run_local_server(port=0)

        # Guarda las credenciales para la próxima ejecución
        with open(token_path, 'w') as token_file:
            token_file.write(creds.to_json())

    # Construye el objeto de servicio para interactuar
    # con la API de Google Calendar
    service = build('calendar', 'v3', credentials=creds)

    # Identificador del calendario que deseas modificar.
    # 'primary' se refiere al calendario principal del usuario.
    calendar_id = 'primary'

    # Crea el evento utilizando la funcionalidad Quick Add
    event = service.events().quickAdd(
        calendarId=calendar_id, text=quick_add_text).execute()

    print('Event created: %s' % (event.get('htmlLink')))

    return event


# @tool
# def send_message(message: str):
#     """Use this function when you need to communicate with the user."""
#     # Configuración del bot
#     load_dotenv()
#     API_TOKEN_BOT = os.getenv("API_TOKEN_BOT")
#     bot = telebot.TeleBot(API_TOKEN_BOT)
#
#     bot.send_message(chat_id="5076346205", text=message)
#

@tool
def send_message(message: str):
    """Use this function when you need to communicate with Cristian."""
    # Configuración del bot
    load_dotenv()
    API_TOKEN_BOT = os.getenv("API_TOKEN_BOT")
    bot = telebot.TeleBot(API_TOKEN_BOT)

    # Escapar caracteres especiales en Markdown
    from telebot.util import escape_markdown
    safe_message = escape_markdown(message)

    # Enviar mensaje usando MarkdownV2
    bot.send_message(chat_id="5076346205", text=safe_message,
                     parse_mode="Markdown")


@tool
def get_company_info(prompt: str) -> str:
    """
    Use this function when you need more information
    about the services offered by OneCluster.
    """
    file_path: str = 'onecluster_info.pdf'

    docs_split: list = load_split_docs(file_path)
    embeddings_model = load_embeddins()
    llm = load_llm_openai()
    create_verctorstore(
        docs_split,
        embeddings_model,
        file_path
    )
    retriever = create_retriever(
        embeddings_model,
        persist_directory="embeddings/onecluster_info"
    )
    qa = create_rag_chain(
        llm, retriever)

    # prompt: str = "Escribe un parrarfo describiendo cuantos son y
    # cuales son los servicios que ofrece OneCluster
    # y brinda detalles sobre cada uno."
    response = qa.invoke(
        {"input": prompt, "chat_history": []}
    )

    return response["answer"]


@tool
def get_current_date_and_time():
    """
    Use this function when you need to know the current date and time.

    Returns:
        str: Current date and time in Bogotá, Colombia.
    """
    bogota_tz = pytz.timezone('America/Bogota')
    current_date_and_time = datetime.now(bogota_tz)
    return current_date_and_time.strftime('%Y-%m-%d %H:%M:%S')