import datetime
import json
import uuid

# Register NetAlertX modules 
import conf
from const import applicationPath, logPath, apiPath, confFileName, sql_generateGuid
from logger import logResult, mylog, print_log
from helper import  timeNowTZ

#-------------------------------------------------------------------------------
# Execution object handling
#-------------------------------------------------------------------------------
class AppEvent_obj:
    def __init__(self, db):
        self.db = db

        # drop table 
        self.db.sql.execute("""DROP TABLE IF EXISTS "AppEvents" """)

        # Drop all triggers
        self.db.sql.execute('DROP TRIGGER IF EXISTS trg_create_device;')
        self.db.sql.execute('DROP TRIGGER IF EXISTS trg_read_device;')
        self.db.sql.execute('DROP TRIGGER IF EXISTS trg_update_device;')
        self.db.sql.execute('DROP TRIGGER IF EXISTS trg_delete_device;')

        self.db.sql.execute('DROP TRIGGER IF EXISTS trg_delete_plugin_object;')
        self.db.sql.execute('DROP TRIGGER IF EXISTS trg_create_plugin_object;')
        self.db.sql.execute('DROP TRIGGER IF EXISTS trg_update_plugin_object;')

        # Create AppEvent table if missing
        self.db.sql.execute("""CREATE TABLE IF NOT EXISTS "AppEvents" (
            "Index"                 INTEGER,
            "GUID"                  TEXT UNIQUE,
            "DateTimeCreated"       TEXT,
            "ObjectType"            TEXT, -- ObjectType (Plugins, Notifications, Events)
            "ObjectGUID"            TEXT,
            "ObjectPlugin"          TEXT,
            "ObjectPrimaryID"       TEXT,
            "ObjectSecondaryID"     TEXT,
            "ObjectForeignKey"      TEXT,
            "ObjectIndex"           TEXT,            
            "ObjectIsNew"           BOOLEAN, 
            "ObjectIsArchived"      BOOLEAN, 
            "ObjectStatusColumn"    TEXT, -- Status (Notifications, Plugins), eve_EventType (Events)
            "ObjectStatus"          TEXT, -- new_devices, down_devices, events, new, watched-changed, watched-not-changed, missing-in-last-scan, Device down, New Device, IP Changed, Connected, Disconnected, VOIDED - Disconnected, VOIDED - Connected, <missing event>            
            "AppEventType"          TEXT, -- "create", "update", "delete" (+TBD)
            "Helper1"               TEXT,
            "Helper2"               TEXT,
            "Helper3"               TEXT,
            "Extra"                 TEXT,            
            PRIMARY KEY("Index" AUTOINCREMENT)
        );
        """)

        # -------------
        # Device events

        sql_devices_mappedColumns = '''
                    "GUID",
                    "DateTimeCreated",
                    "ObjectType",
                    "ObjectPrimaryID",
                    "ObjectSecondaryID",
                    "ObjectStatus",
                    "ObjectStatusColumn",
                    "ObjectIsNew",
                    "ObjectIsArchived",
                    "ObjectForeignKey",
                    "AppEventType"
        '''

        # Trigger for create event
        self.db.sql.execute(f'''
            CREATE TRIGGER IF NOT EXISTS "trg_create_device"
            AFTER INSERT ON "Devices"
            BEGIN
                INSERT INTO "AppEvents" (
                    {sql_devices_mappedColumns}
                )
                VALUES (                    
                    {sql_generateGuid},
                    DATETIME('now'),
                    'Devices',
                    NEW.devMac,
                    NEW.devLastIP,
                    CASE WHEN NEW.devPresentLastScan = 1 THEN 'online' ELSE 'offline' END,
                    'devPresentLastScan',
                    NEW.devIsNew,
                    NEW.devIsArchived,
                    NEW.devMac,
                    'create'
                );
            END;
        ''')

        # 🔴 This would generate too many events, disabled for now
        # # Trigger for read event
        # self.db.sql.execute('''
        #     TODO
        # ''')

        # Trigger for update event
        self.db.sql.execute(f'''
            CREATE TRIGGER IF NOT EXISTS "trg_update_device"
            AFTER UPDATE ON "Devices"
            BEGIN
                INSERT INTO "AppEvents" (
                    {sql_devices_mappedColumns}
                )
                VALUES (                    
                    {sql_generateGuid},
                    DATETIME('now'),
                    'Devices',
                    NEW.devMac,
                    NEW.devLastIP,
                    CASE WHEN NEW.devPresentLastScan = 1 THEN 'online' ELSE 'offline' END,
                    'devPresentLastScan',
                    NEW.devIsNew,
                    NEW.devIsArchived,
                    NEW.devMac,
                    'update'
                );
            END;
        ''')

        # Trigger for delete event
        self.db.sql.execute(f'''
            CREATE TRIGGER IF NOT EXISTS "trg_delete_device"
            AFTER DELETE ON "Devices"
            BEGIN
                INSERT INTO "AppEvents" (
                    {sql_devices_mappedColumns}
                )
                VALUES (                    
                    {sql_generateGuid},
                    DATETIME('now'),
                    'Devices',
                    OLD.devMac,
                    OLD.devLastIP,
                    CASE WHEN OLD.devPresentLastScan = 1 THEN 'online' ELSE 'offline' END,
                    'devPresentLastScan',
                    OLD.devIsNew,
                    OLD.devIsArchived,
                    OLD.devMac,
                    'delete'
                );
            END;
        ''')


        # -------------
        # Plugins_Objects events

        sql_plugins_objects_mappedColumns = '''
                    "GUID",
                    "DateTimeCreated",
                    "ObjectType",                    
                    "ObjectPlugin",
                    "ObjectPrimaryID",
                    "ObjectSecondaryID",
                    "ObjectForeignKey",
                    "ObjectStatusColumn",
                    "ObjectStatus",
                    "AppEventType"
        '''

        # Create trigger for update event on Plugins_Objects
        self.db.sql.execute(f'''
            CREATE TRIGGER IF NOT EXISTS trg_update_plugin_object
            AFTER UPDATE ON Plugins_Objects
            BEGIN
                INSERT INTO AppEvents (
                   {sql_plugins_objects_mappedColumns}
                )
                VALUES (
                    {sql_generateGuid},
                    DATETIME('now'),
                    'Plugins_Objects',                    
                    NEW.Plugin,
                    NEW.Object_PrimaryID,
                    NEW.Object_SecondaryID,
                    NEW.ForeignKey,
                    'Status',
                    NEW.Status,
                    'update'
                );
            END;
        ''')

        # Create trigger for CREATE event on Plugins_Objects
        self.db.sql.execute(f'''
            CREATE TRIGGER IF NOT EXISTS trg_create_plugin_object
            AFTER INSERT ON Plugins_Objects
            BEGIN
                INSERT INTO AppEvents (
                {sql_plugins_objects_mappedColumns}
                )
                VALUES (
                    {sql_generateGuid},
                    DATETIME('now'),
                    'Plugins_Objects',
                    NEW.Plugin,
                    NEW.Object_PrimaryID,
                    NEW.Object_SecondaryID,
                    NEW.ForeignKey,
                    'Status',
                    NEW.Status,
                    'create'
                );
            END;
        ''')

        # Create trigger for DELETE event on Plugins_Objects
        self.db.sql.execute(f'''
            CREATE TRIGGER IF NOT EXISTS trg_delete_plugin_object
            AFTER DELETE ON Plugins_Objects
            BEGIN
                INSERT INTO AppEvents (
                {sql_plugins_objects_mappedColumns}
                )
                VALUES (
                    {sql_generateGuid},
                    DATETIME('now'),
                    'Plugins_Objects',
                    OLD.Plugin,
                    OLD.Object_PrimaryID,
                    OLD.Object_SecondaryID,
                    OLD.ForeignKey,
                    'Status',
                    OLD.Status,
                    'delete'
                );
            END;
        ''')

        self.save()

    # -------------------------------------------------------------------------------
    # -------------------------------------------------------------------------------
    # below code is unused
    # -------------------------------------------------------------------------------

    # Create a new DB entry if new notifications are available, otherwise skip
    def create(self, Extra="", **kwargs):
        # Check if nothing to report, end
        if not any(kwargs.values()):
            return False

        # Continue and save into DB if notifications are available
        self.GUID = str(uuid.uuid4())
        self.DateTimeCreated = timeNowTZ()
        self.ObjectType = "Plugins"  # Modify ObjectType as needed

        # Optional parameters
        self.ObjectGUID         = kwargs.get("ObjectGUID", "")
        self.ObjectPlugin       = kwargs.get("ObjectPlugin", "")
        self.ObjectMAC          = kwargs.get("ObjectMAC", "")
        self.ObjectIP           = kwargs.get("ObjectIP", "")
        self.ObjectPrimaryID    = kwargs.get("ObjectPrimaryID", "")
        self.ObjectSecondaryID  = kwargs.get("ObjectSecondaryID", "")
        self.ObjectForeignKey   = kwargs.get("ObjectForeignKey", "")
        self.ObjectIndex        = kwargs.get("ObjectIndex", "")
        self.ObjectRowID        = kwargs.get("ObjectRowID", "")
        self.ObjectStatusColumn = kwargs.get("ObjectStatusColumn", "")
        self.ObjectStatus       = kwargs.get("ObjectStatus", "")

        self.AppEventStatus = "new"  # Modify AppEventStatus as needed
        self.Extra = Extra

        self.upsert()

        return True

    def upsert(self):
        self.db.sql.execute("""
            INSERT OR REPLACE INTO AppEvents (
                "GUID", 
                "DateTimeCreated", 
                "ObjectType", 
                "ObjectGUID", 
                "ObjectPlugin", 
                "ObjectMAC", 
                "ObjectIP", 
                "ObjectPrimaryID", 
                "ObjectSecondaryID", 
                "ObjectForeignKey", 
                "ObjectIndex", 
                "ObjectRowID", 
                "ObjectStatusColumn", 
                "ObjectStatus", 
                "AppEventStatus", 
                "Extra"
            )
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
        """, (
            self.GUID, 
            self.DateTimeCreated, 
            self.ObjectType, 
            self.ObjectGUID, 
            self.ObjectPlugin, 
            self.ObjectMAC, 
            self.ObjectIP, 
            self.ObjectPrimaryID, 
            self.ObjectSecondaryID, 
            self.ObjectForeignKey, 
            self.ObjectIndex, 
            self.ObjectRowID, 
            self.ObjectStatusColumn, 
            self.ObjectStatus, 
            self.AppEventStatus, 
            self.Extra
        ))

        self.save()

    def save(self):
        # Commit changes
        self.db.commitDB()


def getPluginObject(**kwargs):

    # Check if nothing, end
    if not any(kwargs.values()):
            return None

    # Optional parameters
    GUID          = kwargs.get("GUID", "")
    Plugin        = kwargs.get("Plugin", "")
    MAC           = kwargs.get("MAC", "")
    IP            = kwargs.get("IP", "")
    PrimaryID     = kwargs.get("PrimaryID", "")
    SecondaryID   = kwargs.get("SecondaryID", "")
    ForeignKey    = kwargs.get("ForeignKey", "")
    Index         = kwargs.get("Index", "")
    RowID         = kwargs.get("RowID", "")

    # we need the plugin
    if Plugin == "":
        return None

    plugins_objects = apiPath + 'table_plugins_objects.json'

    try:
        with open(plugins_objects, 'r') as json_file:

            data = json.load(json_file)

            for item in data.get("data",[]):
                if item.get("Index") == Index:
                    return item

            for item in data.get("data",[]):
                if item.get("ObjectPrimaryID") == PrimaryID and item.get("ObjectSecondaryID") == SecondaryID:                    
                    return item
            
            for item in data.get("data",[]):
                if item.get("ObjectPrimaryID") == MAC and item.get("ObjectSecondaryID") == IP:
                    return item

            for item in data.get("data",[]):
                if item.get("ObjectPrimaryID") == PrimaryID and item.get("ObjectSecondaryID") == IP:
                    return item

            for item in data.get("data",[]):
                if item.get("ObjectPrimaryID") == MAC and item.get("ObjectSecondaryID") == IP:
                    return item
                

            mylog('debug', [f'[{module_name}] ⚠ ERROR - Object not found - GUID:{GUID} | Plugin:{Plugin} | MAC:{MAC} | IP:{IP} | PrimaryID:{PrimaryID} | SecondaryID:{SecondaryID} | ForeignKey:{ForeignKey} | Index:{Index} | RowID:{RowID} '])  

            return None

    except (FileNotFoundError, json.JSONDecodeError, ValueError) as e:
        # Handle the case when the file is not found, JSON decoding fails, or data is not in the expected format
        mylog('none', [f'[{module_name}] ⚠ ERROR - JSONDecodeError or FileNotFoundError for file {plugins_objects}'])                

        return None

