# (c) cavaliba.com - home - log.py

# import logging
# #logging.basicConfig(format="%(levelname)s:%(name)s:%(message)s")
# logging.basicConfig(
#      format="{asctime} - {levelname} - {name} - {message}",
#      style="{",
#      datefmt="%Y-%m-%d %H:%M",
# )
# logger = logging.getLogger(__name__)
# console_handler = logging.StreamHandler()
# logger.addHandler(console_handler)
# formatter = logging.Formatter(
#     "{asctime} - {levelname} - {message}",
#      style="{",
#      datefmt="%Y-%m-%d %H:%M",
# )
# console_handler.setFormatter(formatter)
# new V3.19
import logging
from datetime import timedelta

from app_home.configuration import get_configuration
from django.utils import timezone

from .models import CavalibaLog


def get_logger(name):

    logger = logging.getLogger(name)
    logger.setLevel("DEBUG")
    #formatter = logging.Formatter("{asctime} {levelname} {message}", style="{")
    formatter = logging.Formatter("{levelname} {message}", style="{")
    console_handler = logging.StreamHandler()
    console_handler.setLevel("DEBUG")
    console_handler.setFormatter(formatter)
    logger.addHandler(console_handler)
    return logger

logger = get_logger(__name__)

TRACE = "TRACE"
DEBUG = "DEBUG"
INFO = "INFO"
WARNING = "WARNING"
ERROR = "ERROR"
CRITICAL = "CRITICAL"


def log(level, app="", view="", action="", status="", data="", aaa=None, user_ip="-"):

    log_to_db = True
    log_to_sys = False


    alog = CavalibaLog()

    alog.created_at = timezone.now()
    alog.level = level

    alog.app = app
    alog.view = view
    alog.action = action
    alog.status = status
    alog.data = data

    if aaa:
        alog.user_ip = aaa.get("user_ip", "-")
        # switch in Logs, username is source account, impersonate is impersonated account
        if len(aaa.get("impersonate_by",""))>0:
            alog.impersonate = aaa.get("username","-")
            alog.username = aaa["impersonate_by"]
        else:
            alog.impersonate = "-"
            alog.username = aaa.get("username","")
    else:
        alog.impersonate = "-"
        alog.username = "-"
        alog.user_ip = user_ip


    # exclude some IPs
    log_excluded_ip = get_configuration(appname="home", keyname="LOG_EXCLUDED_IP").split(' ')
    if '*' in log_excluded_ip:
        log_to_db = False

    if len(log_excluded_ip) > 0:
        if alog.user_ip in log_excluded_ip:
            log_to_db = False

    # app logging to DB
    # -----------------
    # NEXT : config switch
    if level == "TRACE":
        log_to_db = False

    if level == "DEBUG":
        log_debug = get_configuration(appname="home", keyname="LOG_DEBUG")
        if log_debug != "yes":
            log_to_db = False

    if log_to_db:
        try:
            alog.save()
        except Exception as e:
            print(e)

    # system logging
    #  --------------
    if log_to_sys:

        logstring = f"{alog.user_ip} s={status} app={app} view={view} action={action} user={alog.username}/{alog.impersonate} - {data}"
        #logstring = f"{data}"

        if level == TRACE:
            logger.debug(logstring)
        elif level == DEBUG:
            logger.debug(logstring)
        elif level == INFO:
            logger.info(logstring)
        elif level == WARNING:
            logger.warning(logstring)
        elif level == ERROR:
            logger.error(logstring)
        elif level == CRITICAL:
            logger.critical(logstring)






# ----------------

def log_get_count():
    return CavalibaLog.objects.count()

def log_list(start=0, size=100):
    return CavalibaLog.objects.all().order_by("-id")[start:size]


# ----------------

def purge_all():

    count = CavalibaLog.objects.all().delete()[0]
    log(WARNING, app="CLI_manage", view="log", action="purge_all", status="OK", data=f"{count} entries removed")
    return count


def purge_level(level, keep_days):

    if keep_days == 0:
        count = CavalibaLog.objects.filter(level=level).delete()[0]
    else:
        count = CavalibaLog.objects.filter(level=level, created__lte=timezone.now()-timedelta(days=keep_days)).delete()[0]

    return count


def purge(aaa=None):

    count = 0

    # DEBUG
    keep_days = int(get_configuration(appname="home", keyname="LOG_KEEP_DAYS_DEBUG"))
    subcount = purge_level("DEBUG", keep_days)
    log(INFO, app="home", view="log", action="purge", status="OK", data=f"{subcount} DEBUG entries removed")
    count += subcount

    # INFO
    keep_days = int(get_configuration(appname="home", keyname="LOG_KEEP_DAYS_INFO"))
    subcount = purge_level("INFO", keep_days)
    log(INFO, app="home", view="log", action="purge", status="OK", data=f"{subcount} INFO entries removed")
    count += subcount

    # WARNING
    keep_days = int(get_configuration(appname="home", keyname="LOG_KEEP_DAYS_WARNING"))
    subcount = purge_level("WARNING", keep_days)
    log(INFO, app="home", view="log", action="purge", status="OK", data=f"{subcount} WARNING entries removed")
    count += subcount

    # ERROR
    keep_days = int(get_configuration(appname="home", keyname="LOG_KEEP_DAYS_ERROR"))
    subcount = purge_level("ERROR", keep_days)
    log(INFO, app="home", view="log", action="purge", status="OK", data=f"{subcount} ERROR entries removed")
    count += subcount


    # CRITICAL
    keep_days = int(get_configuration(appname="home", keyname="LOG_KEEP_DAYS_CRITICAL"))
    subcount = purge_level("CRITICAL", keep_days)
    log(INFO, app="home", view="log", action="purge", status="OK", data=f"{subcount} CRITICAL entries removed")
    count += subcount

    # MAX Lines
    keep_entries = int(get_configuration(appname="home", keyname="LOG_MAX_ENTRIES"))
    logcount = CavalibaLog.objects.count()
    if logcount > keep_entries:
        lastitem = CavalibaLog.objects.order_by("-id").first()
        if lastitem:
            lastid = lastitem.id
            cutoff = lastid - keep_entries - 2
            if cutoff > 0:
                subcount = CavalibaLog.objects.filter(id__lt=cutoff).delete()[0]
            else:
                subcount = 0
        log(INFO, app="home", view="log", action="purge", status="OK", data=f"{subcount} MAXLINE entries removed")
        count += subcount

    return count




