import asyncio
import aiosqlite
import os

import pandas as pd

from estaticos.esquema_bd import *

import datetime
import sqlite3 as sql
import io 

RUTA_BD = f"{os.getcwd()}/atwsp.db"


async def crear_bd_async():
    conexion = await aiosqlite.connect(RUTA_BD)

    return conexion


async def crear_tabla(conexion, tabla, **campos):
    '''
    Funcion que se encarga de crear una tabla en la base de datos correspondiente al cursor dado.

    :param conexion: (SQLite3) Un objeto de SQLite3 para manejar la base de datos
    :param tabla: (str) El nombre de la tabla
    :param campos: (dict) Un diccionario con el formato {'campo':'tipo_de_campo'}
    :return: None
    '''

    cursor = await conexion.cursor()

    _campos = ""

    # Creamos el string para la creacion de la tabla en la base de datos
    for campo, tipo in campos.items():
        if campo=='id_mensaje':
            _campos += f"{campo} {tipo} PRIMARY KEY, "
        else:
            _campos += f"{campo} {tipo} ,"

    _campos = f"({_campos[:-2]})"  # Sacamos la ultima coma

    _tabla = f"CREATE TABLE {tabla}"

    tabla_str = f"{_tabla} {_campos}"

    print("1111:",tabla_str)
    # tabla_str = tabla_str + ' CONSTRAINT id_mensaje_pk PRIMARY KEY (id_mensaje)'
    try:
        await cursor.execute(tabla_str)  # Ejecutamos la creacion de la tabla
    except Exception as error:
        if f'table {tabla} already exists' in str(error):
            return

    await conexion.commit()


async def insertar(conexion, tabla, *values):
    '''
    Funcion que se encarga de insertar en una tabla dada los campos otorgados.

    :param conexion: (SQLite3) Un objeto de SQLite3 para manejar la base de datos
    :param tabla: (str) El nombre de la tabla
    :param values: (list) Una lista con los valores a insertar en la tabla.
    :return: None
    '''

    try:
        cursor = await conexion.cursor()

        values = f"({str(values).rstrip(',)').lstrip('(')})"

        insertar_str = f"INSERT INTO {tabla} VALUES {values}"

        await cursor.execute(insertar_str)

        await conexion.commit()

        return cursor.lastrowid

    except:
        return None


async def obtener_todos(conexion, tabla):
    '''
    Esta funcion se encarga de obtener todos los registros de una tabla dada.

    :param conexion: (SQLite3) Un objeto de SQLite3 para manejar la base de datos
    :param tabla: (str) El nombre de la tabla
    :return: (list) Una lista con los resultados de la query
    '''

    cursor = await conexion.cursor()

    await cursor.execute(f"SELECT rowid,* FROM {tabla}")

    registros = await cursor.fetchall()

    return registros


async def obtener_pendientes(conexion):
    '''
    Esta funcion se encarga de obtener los mensajes que se encuentran con estado 'pendiente' para enviar al servidor.

    :param conexion: (SQLite3) Un objeto de SQLite3 para manejar la base de datos
    :return:
    '''

    cursor = await conexion.execute("SELECT * FROM mensajes WHERE estado='pendiente'")

    registros = await cursor.fetchall()

    return registros


async def actualizar_estado_mensaje(conexion, id_mensaje, estado):
    '''
    Esta funcion se encarga de actualizar el estado de un mensaje dado.
    :param conexion: (SQLite3) Un objeto de SQLite3 para manejar la base de datos
    :param id_mensaje: (str) La id unica de un mensaje de WhatsApp
    :param estado: (str) El estado de un mensaje
    :return:
    '''

    cursor = await conexion.cursor()

    actualizar_str = "UPDATE mensajes SET estado=? WHERE id_mensaje=?"

    await cursor.execute(actualizar_str, (estado, id_mensaje))

    await conexion.commit()


async def obtener_mensajes_a_enviar(conexion):
    '''
    Esta funcion se encarga de obtener todos los mensajes para enviar a traves de selenium.

    :param conexion: (SQLite3) Un objeto de SQLite3 para manejar la base de datos
    :return:
    '''

    cursor = await conexion.execute("SELECT rowid,* FROM fila WHERE estado=?", ['pendiente'])

    registros = await cursor.fetchall()

    return registros


async def obtener_id_ultimo_mensaje(conexion, telefono):
    '''
    Esta funcion se  encarga de obtener el id del ultimo mensaje enviado por un usuario.

    :param conexion: (SQLite3) Un objeto de SQLite3 para manejar la base de datos
    :param telefono: (str) El telefono del usuario.
    :return:
    '''

    busqueda = "SELECT * FROM mensajes WHERE telefono=?"

    cursor = await conexion.execute(busqueda, [telefono])

    registros = await cursor.fetchall()

    return registros


async def obtener_usuario(conexion, telefono):
    busqueda = "SELECT * FROM usuarios WHERE telefono=?"

    cursor = await conexion.execute(busqueda, [telefono])

    registro = await cursor.fetchone()

    return registro


async def actualizar_mensaje_fila(conexion, id_mensaje, estado):
    cursor = await conexion.cursor()

    actualizar_str = "UPDATE fila SET estado=? WHERE rowid=?"

    await cursor.execute(actualizar_str, (estado, id_mensaje))

    await conexion.commit()


async def actualizar_contacto(conexion, telefono, nombre_agendado):
    cursor = await conexion.cursor()

    actualizar_str = "UPDATE usuarios SET nombre_agendado=? WHERE telefono=?"

    await cursor.execute(actualizar_str, (nombre_agendado, telefono))

    await conexion.commit()


async def bootstrap_bd():

    print("inicializando base datos")
    conexion = await crear_bd_async()

    await crear_tabla(conexion, 'mensajes', **esq_mensajes)

    await crear_tabla(conexion, 'fila', **esq_fila)

    return conexion


def crear_string_insert(*args, tabla=None):
    """Se encarga de crear un string utilizado para un insert en la base de datos"""

    # print("denro crear:",args)
    values = f"({str(args).rstrip(',)').lstrip('(')})"

    # print("voy a insertar estos valores:",values)
    return f"INSERT INTO {tabla} VALUES {values}"

# Verifica si existen registros que tengan 2 dias o mas de antiguedad
def verificar_registros():  # conexion, cursor :v
    dia_delta = datetime.timedelta(days=-2)  # Para hacer una resta paso un entero negativo
    fecha_hoy = datetime.date.today()  # fecha actual
    fecha_limite = fecha_hoy + dia_delta  # Resto 2 dias a la fecha actual
    fecha_lim_formatada = datetime.datetime.strftime(fecha_limite, '%Y/%m/%d %H:%M')  # formato para tabla 'mensajes'

    conexion = sql.connect(RUTA_BD)  # :v
    cursor = conexion.cursor()  # :v
    query = f"SELECT * FROM mensajes WHERE fecha_hora <= '{fecha_lim_formatada}'"
    cursor.execute(query)
    registros = cursor.fetchall()
    conexion.commit()
    # conexion.close() esto no va
    if len(registros) > 0:
        print("hay registros viejos")
        # Llamo a crear_backup()
        respuesta = crear_backup(conexion, fecha_limite)  
        if(respuesta):
            #  Llamar a borrar_registros()
            borrar_registros(conexion, cursor, fecha_lim_formatada)  # posiblemente este sea el return
            return print("Backup creado")
        else:
            print("no se pudo generar el backup", respuesta)    
    else:
        # No hago nada
        return print("No existen registros de mas de 2 dias")
    
def crear_backup(conexion, fecha_lim):  # nombre de la tabla para armar el path??
    print("paso por crear_backup()")
    archivo = f'copias/backup_{fecha_lim}.sql'  # {fecha_lim} Nota: es necesario crear previamente la carpeta 'copias'
    print('nombre del backup', archivo)
    try:
        with io.open(archivo, 'w') as f:
            for dato in conexion.iterdump(): # Por cada iteracion hacemos un salto de linea y agregamos el dato "actual"
                f.write('%s\n' % dato)
        return True         
    except Exception as error:
        return error    

def borrar_registros(conexion, cursor, fecha):
    print("paso por borrar_registros()")
    instruccion = f"DELETE FROM mensajes WHERE fecha_hora <= '{fecha}'"   
    cursor.execute(instruccion)  # Selecciona -SELECT-  
    conexion.commit()  

    fecha = fecha.replace("/", "-")
    fecha = fecha + ":00"  # puede fallar
    fecha_fila = datetime.datetime.strptime(fecha, '%Y-%m-%d %H:%M:%S')  # aber
    print("formato del fecha_hora para tabla fila:", fecha_fila)
    instruccion_fila = f"DELETE FROM fila WHERE fecha_hora <= '{fecha_fila}'"   
    cursor.execute(instruccion_fila)  # Selecciona -SELECT-  
    conexion.commit()
    # conexion.close()





