# author Aaron Moser, Theresa Herr

import sqlite3
import json
import threading
from enum import Enum
from typing import List
from heater.host import Host
from heater_instance_interface import HeaterInstanceInterface
from tasks.task_outcome import TaskOutcome

db_lock = threading.Lock()

class DatabaseOperation(Enum):
    GET_INSTANCE = 1
    GET_INSTANCES = 2
    UPDATE_INSTANCE = 3
    REMOVE_INSTANCE = 4
    REMOVE_ALL_INSTANCES = 5

class DatabaseInterface:
    def __init__(self, db_path):
        self.__db_path = db_path
        self.__init_database()
    
    def access_db(self, operation: DatabaseOperation, args) -> List[Host]:
        db_lock.acquire()
        try:
            if operation == DatabaseOperation.GET_INSTANCE:
                return self.__get_instance(args)
            elif operation == DatabaseOperation.GET_INSTANCES:
                return self.__get_all_instances()
            elif operation == DatabaseOperation.UPDATE_INSTANCE:
                self.__update_instance(args)
            elif operation == DatabaseOperation.REMOVE_INSTANCE:
                self.__remove_instance(args)
            elif operation == DatabaseOperation.REMOVE_ALL_INSTANCES:
                self.__remove_all_instances()
            else:
                print("Unknown database operation: " + operation)
        finally:
            db_lock.release()

    def __init_database(self):
        conn = sqlite3.connect(self.__db_path)
        self.__init_table_instances(conn)
        self.__init_table_task_outcomes(conn)
        conn.close()

    def __init_table_instances(self, conn: sqlite3.Connection):
        cursor = conn.cursor()
        cursor.execute('''
        SELECT name FROM sqlite_master WHERE type='table' AND name='instances';
        ''')
        table_exists = cursor.fetchone()

        if table_exists:
            print("SQLiteDB table 'instances' already exists, skipped initialization of table.")
        else:
            cursor.execute('''
                CREATE TABLE instances (
                    id INTEGER PRIMARY KEY,
                    owner TEXT,
                    calculated_demand BLOB,
                    number_of_persons_in_household INTEGER,
                    fulfilled_demand BLOB,
                    status TEXT,
                    heater_busy_methods BLOB
                )
            ''')
        conn.commit()

    def __init_table_task_outcomes(self, conn: sqlite3.Connection):
        cursor = conn.cursor()
        cursor.execute('''
        SELECT name FROM sqlite_master WHERE type='table' AND name='task_outcomes';
        ''')
        table_exists = cursor.fetchone()

        if table_exists:
            print("SQLiteDB table 'task_outcomes' already exists, skipped initialization of table.")
        else:
            cursor.execute('''
                CREATE TABLE task_outcomes (
                    id INTEGER PRIMARY KEY AUTOINCREMENT,
                    required_workload_in_percent REAL,
                    runtime INTEGER,
                    tries INTEGER,
                    count INTEGER,
                    diff REAL,
                    diff_factor REAL,
                    actual_date TEXT,
                    heat_demand_per_minute REAL,
                    actual_heat_generated REAL,
                    hostname TEXT,
                    username TEXT
                )
            ''')
        conn.commit()

    def __get_instance(self, args) -> List[Host]:
        conn = sqlite3.connect(self.__db_path)
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM instances WHERE id=?", (args,))
        result = cursor.fetchone()
        conn.commit()
        conn.close()
        return result

    def __get_all_instances(self) -> List[Host]:
        conn = sqlite3.connect(self.__db_path)
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM instances")
        result = cursor.fetchall()
        conn.commit()
        conn.close()
        return result

    def __update_instance(self, args):
        conn = sqlite3.connect(self.__db_path)
        cursor = conn.cursor()
        cursor.execute('''
            UPDATE instances
            SET owner=?, calculated_demand=?, number_of_persons_in_household=?, fulfilled_demand=?, status=?, heater_busy_methods=?
            WHERE id=?
        ''', (args['owner'], args['calculated_demand'], args['number_of_persons_in_household'], args['fulfilled_demand'], args['status'], args['heater_busy_methods'], args['id']))
        conn.commit()
        conn.close()

    def __remove_instance(self, args):
        conn = sqlite3.connect(self.__db_path)
        cursor = conn.cursor()
        cursor.execute("DELETE FROM instances WHERE id=?", (args,))
        conn.commit()
        conn.close()

    def __remove_all_instances(self):
        conn = sqlite3.connect(self.__db_path)
        cursor = conn.cursor()
        cursor.execute("DELETE FROM instances")
        conn.commit()
        conn.close()

    def _get_instances(self) -> List[HeaterInstanceInterface]:
        conn = sqlite3.connect(self.__db_path)
        cursor = conn.cursor()
        cursor.execute("SELECT * FROM instances")
        potential_instances = cursor.fetchall()
        conn.commit()
        conn.close()

        # Transform potential instances to objects which application can communicate with
        instance_objects = []
        for instance in potential_instances:
            id, owner, calculated_demand, number_of_persons_in_household, fulfilled_demand, status, heater_busy_methods = instance
            calculated_demand_json = json.loads(calculated_demand)
            fulfilled_demand_json = json.loads(fulfilled_demand)
            heater_busy_methods_json = json.loads(heater_busy_methods)
            host = Host(hostname=id, username=owner, ssh_key_filename="", passphrase="")
            heater_instance_interface = HeaterInstanceInterface(host)
            instance_objects.append(heater_instance_interface)

        return instance_objects

    def insert_task_outcome_into_instances_table(self, heater_instance_interface: HeaterInstanceInterface, outcome_list: List[TaskOutcome]):
        conn = sqlite3.connect(self.__db_path)
        cursor = conn.cursor()

        energy_demands = []
        fulfilled_demands = []

        for outcome in outcome_list:
            energy_demands.append({
                "date": outcome.get_actual_date().strftime("%d.%m.%Y"),
                "energy_demand_in_watts": outcome.get_heat_demand_per_minute() * 60  # assuming demand per minute
            })
            fulfilled_demands.append({
                "date": outcome.get_actual_date().strftime("%d.%m.%Y"),
                "scored_energy_demand_in_watts": outcome.get_actual_heat_generated() * 60,  # assuming generated heat per minute
                "busy_method_used": ["hpc"]  # example method used
            })

        calculated_demand_json = {"energy_demands": energy_demands}
        fulfilled_demand_json = {"scored_demand": fulfilled_demands}
        calculated_demand_blob = json.dumps(calculated_demand_json).encode('utf-8')
        fulfilled_demand_blob = json.dumps(fulfilled_demand_json).encode('utf-8')
        heater_busy_methods_blob = json.dumps({"busy_methods_active": ["hpc"]}).encode('utf-8')

        cursor.execute('''
            INSERT INTO instances (
                owner, calculated_demand, number_of_persons_in_household, 
                fulfilled_demand, status, heater_busy_methods
            ) VALUES (?, ?, ?, ?, ?, ?)
        ''', (
            heater_instance_interface.get_host().username,
            calculated_demand_blob,
            3,  # assuming a default value, should be dynamic
            fulfilled_demand_blob,
            'online',
            heater_busy_methods_blob
        ))

        conn.commit()
        conn.close()

    def insert_task_outcome(self, outcome: TaskOutcome):
        conn = sqlite3.connect(self.__db_path)
        cursor = conn.cursor()
        cursor.execute('''
            INSERT INTO task_outcomes (
                required_workload_in_percent, runtime, tries, count, diff, diff_factor, actual_date, heat_demand_per_minute, actual_heat_generated, hostname, username
            ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        ''', (
            outcome.get_required_workload_in_percent(),
            outcome.get_runtime(),
            outcome.get_tries(),
            outcome.get_count(),
            outcome.get_diff(),
            outcome.get_diff_factor(),
            outcome.get_actual_date().strftime("%Y-%m-%d %H:%M:%S"),
            outcome.get_heat_demand_per_minute(),
            outcome.get_actual_heat_generated(),
            outcome.get_hostname(),
            outcome.get_username()
        ))
        conn.commit()
        conn.close()

    # def insert_data(self, owner: str, calculated_demand: List[dict], number_of_persons_in_household: int, fulfilled_demand: List[dict], status: str, heater_busy_methods: List[str]):
    #     conn = sqlite3.connect(self.__db_path)
    #     cursor = conn.cursor()

    #     calculated_demand_blob = json.dumps({"energy_demands": calculated_demand}).encode('utf-8')
    #     fulfilled_demand_blob = json.dumps({"scored_demand": fulfilled_demand}).encode('utf-8')
    #     heater_busy_methods_blob = json.dumps({"busy_methods_active": heater_busy_methods}).encode('utf-8')

    #     cursor.execute('''
    #         INSERT INTO instances (
    #             owner, calculated_demand, number_of_persons_in_household, 
    #             fulfilled_demand, status, heater_busy_methods
    #         ) VALUES (?, ?, ?, ?, ?, ?)
    #     ''', (
    #         owner,
    #         calculated_demand_blob,
    #         number_of_persons_in_household,
    #         fulfilled_demand_blob,
    #         status,
    #         heater_busy_methods_blob
    #     ))

    #     conn.commit()
    #     conn.close()