import sys
import io
import datetime
import threading
import queue
import time
import logging

# NetAlertX imports

import conf
from const import *

#-------------------------------------------------------------------------------
# duplication from helper to avoid circle
#-------------------------------------------------------------------------------
def timeNowTZ():
    if conf.tz:
        return datetime.datetime.now(conf.tz).replace(microsecond=0)
    else:
        return datetime.datetime.now().replace(microsecond=0)

#-------------------------------------------------------------------------------
# Map custom debug levels to Python logging levels
custom_to_logging_levels = {
    'none': logging.NOTSET,
    'minimal': logging.WARNING,
    'verbose': logging.INFO,
    'debug': logging.DEBUG,
    'trace': logging.DEBUG,  # Can map to DEBUG or lower custom level if needed
}

#-------------------------------------------------------------------------------
# More verbose as the numbers go up
debugLevels = [
    ('none', 0), ('minimal', 1), ('verbose', 2), ('debug', 3), ('trace', 4)
]

# use the LOG_LEVEL from the config, may be overridden
currentLevel = conf.LOG_LEVEL

# tracking log levels
setLvl = 0  
reqLvl = 0  

#-------------------------------------------------------------------------------
class Logger:
    def __init__(self, LOG_LEVEL='verbose'):        
        global currentLevel

        currentLevel = LOG_LEVEL

        # Automatically set up custom logging handler
        self.setup_logging()

    def setup_logging(self):
        root_logger = logging.getLogger()
        # Clear existing handlers to prevent duplicates
        if root_logger.hasHandlers():
            root_logger.handlers.clear()

        # Create the custom handler
        my_log_handler = MyLogHandler()
        # my_log_handler.setLevel(custom_to_logging_levels.get(currentLevel, logging.NOTSET))

        # Optional: Add a formatter for consistent log message format
        # formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        formatter = logging.Formatter('%(message)s', datefmt='%H:%M:%S')
        my_log_handler.setFormatter(formatter)

        # Attach the handler to the root logger
        root_logger.addHandler(my_log_handler)
        root_logger.setLevel(custom_to_logging_levels.get(currentLevel, logging.NOTSET))

# for python logging
class MyLogHandler(logging.Handler):
    def emit(self, record):
        log_entry = self.format(record)
        log_queue.put(log_entry)

def mylog(requestedDebugLevel, n):
    global setLvl, reqLvl  

    #  Get debug urgency/relative weight
    for lvl in debugLevels:
        if currentLevel == lvl[0]:
            setLvl = lvl[1]
        if requestedDebugLevel == lvl[0]:
            reqLvl = lvl[1]

    if reqLvl <= setLvl:
        file_print (*n)        

#-------------------------------------------------------------------------------
# Queue for log messages
log_queue = queue.Queue(maxsize=1000)  # Increase size to handle spikes

# Dedicated thread for writing logs
log_thread = None  # Will hold the thread reference

def log_writer():
    buffer = []
    while True:
        try:
            log_entry = log_queue.get(timeout=1)  # Wait for 1 second for logs
            if log_entry is None:  # Graceful exit signal
                break
            buffer.append(log_entry)
            if len(buffer) >= 10:  # Write in batches of 10
                with open(logPath + "/app.log", 'a') as log_file:
                    log_file.write('\n'.join(buffer) + '\n')
                buffer.clear()
        except queue.Empty:
            # Flush buffer periodically if no new logs
            if buffer:
                with open(logPath + "/app.log", 'a') as log_file:
                    log_file.write('\n'.join(buffer) + '\n')
                buffer.clear()

#-------------------------------------------------------------------------------
# Function to start the log writer thread if it doesn't exist
def start_log_writer_thread():
    global log_thread
    if log_thread is None or not log_thread.is_alive():
        log_thread = threading.Thread(target=log_writer, daemon=True)
        log_thread.start()

#-------------------------------------------------------------------------------
def file_print(*args):
    result = timeNowTZ().strftime('%H:%M:%S') + ' '   
    
    for arg in args:                
        result += str(arg)       

    logging.log(custom_to_logging_levels.get(currentLevel, logging.NOTSET), result)  # Forward to Python's logging system 
    print(result)

    # Ensure the log writer thread is running
    start_log_writer_thread()

#-------------------------------------------------------------------------------
def print_log(pText):
    # Check if logging is active
    if not conf.LOG_LEVEL == 'debug':
        return

    # Current Time    
    log_timestamp2 = datetime.datetime.now(conf.tz).replace(microsecond=0)

    # Print line + time + text
    file_print('[LOG_LEVEL=debug]', log_timestamp2.strftime('%H:%M:%S'), pText)
    return pText

#-------------------------------------------------------------------------------
def append_file_binary(file_path, input_data):
    with open(file_path, 'ab') as file:
        if isinstance(input_data, str):
            input_data = input_data.encode('utf-8')  # Encode string as bytes
        file.write(input_data)

#-------------------------------------------------------------------------------
def logResult(stdout, stderr):
    if stderr is not None:
        append_file_binary(logPath + '/stderr.log', stderr)
    if stdout is not None:
        append_file_binary(logPath + '/stdout.log', stdout)

#-------------------------------------------------------------------------------
def append_line_to_file(pPath, pText):
    # append the line using the correct python version
    if sys.version_info < (3, 0):
        file = io.open(pPath, mode='a', encoding='utf-8')
        file.write(pText.decode('unicode_escape'))
        file.close() 
    else:
        file = open(pPath, 'a', encoding='utf-8') 
        file.write(pText)
        file.close()  
