import sqlite3
import secrets
import pandas as pd
from datetime import datetime
from beartype import beartype

'''
    -: database file includes different tables
    -: database table includes different rows
'''

class database_file:
    
    @beartype
    def __init__(self, db_location:str):
        self.db_location = db_location
    
    def get_table(self):
        conn = sqlite3.connect(self.db_location)
        cur = conn.cursor()
        cur.execute("SELECT name FROM sqlite_master WHERE type='table';")
        tables = cur.fetchall()
        columnsName = [i[0] for i in cur.description]
        conn.commit()
        conn.close()
        df_tables = pd.DataFrame(tables, columns=columnsName)
        return df_tables
    
class database_table(database_file):
    @beartype
    def __init__(self, db_location: str, table_name:str):
        super().__init__(db_location)
        self.table_name  = table_name
    
    @beartype
    def create_table(self, keys:list, datatype:list):
        conn = sqlite3.connect(self.db_location)
        cur = conn.cursor()
        # create table
        create_table_sql = "CREATE TABLE IF NOT EXISTS {} ".format(self.table_name)
        keys_datatype = [f"{keys[i]} {datatype[i]}," for i in range(len(keys))]
        create_table_sql = create_table_sql + \
                            "({} id text, date text);".format("".join(keys_datatype))
        cur.execute(create_table_sql)
        
        conn.commit()
        conn.close()
        self.keys = keys
        self.datatype = datatype
        return True
    
    @beartype
    def insert_rows(self, insert_data:list):
        
        conn = sqlite3.connect(self.db_location)
        cur  = conn.cursor()        
        sql = "SELECT * FROM {};".format(self.table_name)
        cur.execute(sql)
        keys = [i[0] for i in cur.description][0:-2]
        
        # insert data
        qs = ",".join(["?" for _ in range(len(keys)+2)])
        insert = """INSERT INTO {} ({},id,date) 
                 VALUES ({})""".format(self.table_name, ",".join(keys), qs)

        # check if lists in insert_data have same length - todo
        for i in range(len(insert_data)):
            insert_data_i = insert_data[i]
            insert_data_i.append(secrets.token_hex(16))
            insert_data_i.append(str(datetime.now())[0:19])
            cur.execute(insert, tuple(insert_data_i))
                
        conn.commit()
        conn.close()
        return True
    
    def get_all(self):
        try:
            conn = sqlite3.connect(self.db_location)
            cur = conn.cursor()
            sql = "SELECT * FROM {};".format(self.table_name)
            cur.execute(sql)
            # get all rows
            rows = cur.fetchall()
            # keys
            columnsName = [i[0] for i in cur.description]
            conn.commit()
            conn.close()
            return pd.DataFrame(rows, columns=columnsName)
        except sqlite3.Error as err:
            return err
    
    @beartype
    def get_one(self, id:str):
        try:
            con = sqlite3.connect(self.db_location)
            cur = con.cursor()
            sql_select = "SELECT * FROM {} WHERE id=?".format(self.table_name)
            cur.execute(sql_select, (id,))
            rows = cur.fetchone()
            columnsName = [i[0] for i in cur.description]
            con.commit()
            con.close()
            return pd.DataFrame([rows], columns=columnsName)
        except sqlite3.Error as err:
            return err
    
    @beartype
    def delete_rows(self, ids:list):
        try:
            for _id in ids:
                con = sqlite3.connect(self.db_location)
                cur = con.cursor()
                sql_delete = 'DELETE FROM {} WHERE id=?'.format(self.table_name)
                cur.execute(sql_delete, (_id,))  
                con.commit()
                con.close()
            return True
        except sqlite3.Error as err:
            return err
    
    def delete_table(self):
        conn = sqlite3.connect(self.db_location)
        cur  = conn.cursor()
        sql = "DROP TABLE {}".format(self.table_name)
        cur.execute(sql)
        conn.commit()
        conn.close()
        return True