# Imports from standard library
import os
from typing import Optional, Union
import asyncio
import time
import sqlite3
import hashlib

# Import these utilities
from utils.merkletree import MerkleTree, MerkleError

# Import the HTTP app server
from fastapi import FastAPI, BackgroundTasks

##################################

from sqlmodel import Field, SQLModel, create_engine

class MerkleTable(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    timestamp: int
    ngsi_id_hash: str
    ngsi_value_hash: str
    ngsi_receipt: Optional[str] = None


# The table scripts

drop_table_script = """
DROP TABLE IF EXISTS testing;
"""

create_table_script = """
CREATE TABLE IF NOT EXISTS testing (
    id INTEGER PRIMARY KEY,
    timestamp INTEGER NOT NULL,
    ngsi_id_hash TEXT NOT NULL,
    ngsi_value_hash TEXT NOT NULL,
    ngsi_receipt TEXT
);
"""

set_journal_wal = 'PRAGMA journal_mode=WAL'
query_journal_mode = """PRAGMA journal_mode"""


class MerkleBuffer:
    def __init__(self, 
        db_name: str = 'mkbuffer.db',   # Name of the database
        db_max_elements: int = 10000,   # Maximum size of database, in number of records
        maxLeaves: int = 1024,          # Maximum number of leaves of the Merkle Tree to notarize
        maxInterval: int = 60,          # Notarize every maxInterval (seconds) even if not enough leaves received yet
        durability: int = 10            # Commit database every durability seconds, to make data permanent
    ) -> None:
        self.db_name = db_name
        self.maxLeaves = maxLeaves
        self.db_max_elements = db_max_elements
        self.maxInterval = maxInterval
        self.durability = durability
        self.next_record = 1
        self.leaves = 0
        self.last_notarization = time.time()

        print(f'MaxLeaves: {maxLeaves}')

        self.open()

        # Create a background task which commits the db every durability secs (or 2 sec as a minimum),
        # and registers the Merkle Tree even if not enough entries have been received
        if durability > 0 or maxInterval > 0:
            self.commit_task = asyncio.create_task(self.commit_background_task(min(durability, maxInterval)))

    def db_name(self):
        self.db_name

    def open(self):

        sqlite_file_name = "sqlmodel.db"
        sqlite_url = f"sqlite:///{sqlite_file_name}"

        engine = 