# -*- coding: utf-8 -*-
"""mywattsmon"""

import sqlite3
import time

COLDEF_PREFIX = ["id INTEGER PRIMARY KEY", "ts TEXT"]

class DbWriter():

    """
    DbWriter class.
    """

    def __init__(self, dbfile:str, tablename:str, columns:dict):
        """Setup.

        Args:
            dbfile (str): Absolute path to the database file.
            tablename (str): The table name.
            columns (dict): The configured columns.

        Returns:
            None.
        """
        self.dbfile = dbfile
        self.tabledef = self.__create_tabledef(tablename, columns)
        self.__create_table(tablename, self.tabledef['coldef'])

    def get_dbfile(self):
        """Gets the absolute path to the database file.

        Args:
           None.
 
        Returns:
           dict: The absolute path to the database file.
        """
        return self.dbfile
        
    def get_tabledef(self):
        """Gets the table definition.

        Args:
           None.
 
        Returns:
           dict: The table definition.
        """
        return self.tabledef
        
    def add_current_values(self, current_values:dict):
        """Adds current power and energy values to the database.

        Args:
           current_values (dict): Current values from the monitor.
 
        Returns:
           int: The row ID generated by SQLite.
        """
        row = {}
        tablename = self.tabledef.get('tablename')
        columns = self.tabledef.get('columns')
        for column in columns:
            units = columns.get(column).get('units')
            var = columns.get(column).get('var')
            row[column] = self.__get_value_for_column(
                units, var, current_values
                )
        return self.__add_row(tablename, row)

    # ---------------
    # Private methods
    # ---------------
        
    def __create_tabledef(self, tablename:str, columns:dict):
        """Creates a table definition from configured columns.
         
        Args:
           tablename (str): The table name.
           columns (dict): The configured columns.
        
        Returns:
           dict: The table definition.
        """
        tabledef = {'tablename':tablename}
        coldef = []
        for column in columns:
            var = columns.get(column).get('var')
            if var == 'power':
                coldef.append(f"{column} INT")
            elif var == 'energy':
                coldef.append(f"{column} REAL")
            else:
                coldef.append(f"{column} TEXT")
        tabledef['coldef'] = ", ".join(coldef)
        tabledef['columns'] = columns
        return tabledef
         
    def __get_value_for_column(self, units:str, var:str, values:dict):
        """Gets a power or energy value from the given unit or unit list.
        
        If a unit list is given, the values are summed up.

        Args:
           units (str): Unit name or CSV list of unit names.
           var (str): Configured variable. Options: 'power', 'energy'.
           values (dict): Current values of all units.
 
        Returns:
           int or float: The value as int or float, or None.
        """
        value = 0
        unitlist = units.replace(' ', '').split(',')
        for unit in unitlist:
            if values.get(unit) is None:
                return None
            code = values[unit].get('code')
            if code is None:
                return None
            if code == 1:
                return None
            val = values[unit].get(var)
            if val is None:
                return None
            if type(val) not in (int, float):
                return None
            if var == 'power':
                value += int(val)
            else:
                value += float(val)
        return value

    def __get_db_conn(self):
        """Gets an SQLite database connection.
        
        Also creates the database if it does not already exist.
        
        Args:
            None.
        
        Returns:
            object: SQL connection.
        """
        return sqlite3.connect(self.dbfile, timeout=10.0)

    def __create_table(self, tablename:str, coldef:str):
        """Creates an SQLite table if it does not already exist.

        Args:
            tablename (str): The table name.
            coldef (str): The SQL column definition.
        
        Returns:
            None
        """
        sql = None
        db = None
        c = None
        try:
            coldef_prefix = ", ".join(COLDEF_PREFIX)
            cols = f"{coldef_prefix}, {coldef}"
            sql = f"CREATE TABLE IF NOT EXISTS {tablename} ({cols});"
            db = self.__get_db_conn()
            c = db.cursor()
            c.execute(sql)
            db.commit()
        finally:
            if c:
                c.close()
            if db:
                db.close()
                
    def __add_row(self, tablename:str, row:dict):
        """Adds a row to an SQLite table.

        Args:
            row (dict): The data column names and values.
        
        Returns:
            int: The row ID generated by SQLite.
        """
        sql = None
        db = None
        c = None
        try:
            # 'id' (first column) is generated by SQLite.
            # 'ts' (second column):
            qmarks = ['?']
            names = ['ts']
            values = [time.strftime("%Y-%m-%d %H:%M", time.localtime())]
            # Append data columns
            for name in row:
                names.append(name)
                qmarks.append("?")
                values.append(row.get(name))
            str_names = ", ".join(names)
            str_qmarks = ", ".join(qmarks)
            sql = f"INSERT INTO {tablename} ({str_names}) "\
                  f"VALUES ({str_qmarks});"
            db = self.__get_db_conn()
            c = db.cursor()
            c.execute(sql, values)
            db.commit()
            return c.lastrowid
        finally:
            if c:
                c.close()
            if db:
                db.close()
