# (c) cavaliba.com - app_status - common.py

import re
import time
from datetime import datetime, timedelta

import requests
from django.db.models import Sum
from django.utils import timezone

from app_data.data import Instance
from app_home.configuration import get_configuration
from app_status.models import StatusRaw, StatusSampleDay, StatusSampleHour

requests.packages.urllib3.disable_warnings()


# --------------------------------------------------------
# Helpers
# --------------------------------------------------------


def get_monitor(iname=None):

    instance = Instance.from_keyname(classname="status_monitor", keyname=iname)
    if not instance:
        return
    itype = instance.get_attribute_first("type")
    if itype == "HTTP":
        monitor = MonitorHTTP(instance=instance)
    else:
        monitor = Monitor(instance=instance)
    return monitor


def get_monitors():

    reply2 = []

    for instance in Instance.iterate_classname(classname="status_monitor", enabled=True):
        itype = instance.get_attribute_first("type")
        if itype == "HTTP":
            monitor = MonitorHTTP(instance=instance)
        else:
            monitor = Monitor(instance=instance)
        reply2.append(monitor)
    return reply2


def get_monitor_names():
    return [
        i.keyname for i in Instance.iterate_classname(classname="status_monitor", enabled="yes")
    ]


def run_monitors(monitors=None):

    # list of Class MonitorXXXXX
    if not monitors:
        monitors = get_monitors()

    count = 0
    for monitor in monitors:
        monitor.run()
        if not monitor.run_done:
            continue

        count += 1
        obj = StatusRaw()
        obj.monitor = monitor.keyname
        if monitor.run_error:
            obj.status = False
        else:
            obj.status = True

        obj.data = monitor.run_error
        obj.usec = monitor.run_duration
        obj.save()

    return count


# -----------------------------------------------
# live
# -----------------------------------------------
def compute_live_status(monitors=None):

    # list of Class MonitorXXXXX
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(minutes=5)
    raws = StatusRaw.objects.filter(timestamp__gte=delta).order_by("-timestamp")

    for monitor in monitors:
        monitor.live_status = "NA"
        for raw in raws:
            if raw.monitor == monitor.keyname:
                if monitor.live_status == "NA":
                    if raw.status:
                        monitor.live_status = "OK"
                    else:
                        monitor.live_status = "KO"
                    break

    return monitors


# -----------------------------------------------
# hourly
# -----------------------------------------------
def compute_hourly_ratio(monitors=None):
    """
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    """
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(minutes=60)
    raws = StatusRaw.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for raw in raws:
        if raw.monitor not in table:
            table[raw.monitor] = {}
            table[raw.monitor]["OK"] = 0
            table[raw.monitor]["KO"] = 0
        if raw.status:
            table[raw.monitor]["OK"] += 1
        else:
            table[raw.monitor]["KO"] += 1

    #   compute ratio
    for m, counters in table.items():
        d = table[m]["OK"] + table[m]["KO"]
        if d > 0:
            table[m]["ratio"] = table[m]["OK"] / d
        else:
            table[m]["ratio"] = -1

    # ratio & pretty
    for monitor in monitors:
        monitor.hourly_ratio = -1
        monitor.hourly_ratio_pretty = "NA"

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]["ratio"]

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100 * ratio:.0f} %"
        else:
            pretty = "NA"

        monitor.hourly_ratio = ratio
        monitor.hourly_ratio_pretty = pretty

    return


def compute_hourly_bargraph(monitors=None):
    """
    out:  []  'minute':int(0-59), 'ok':int, 'ko':int, 'ratio':float|None
    """

    WIDTH = 15

    if not monitors:
        monitors = get_monitors()

    for monitor in monitors:
        now = timezone.now()  # django aware (UTC with TZ convert for rendering)
        now_local = datetime.now()
        now1h = now - timedelta(minutes=60)
        raws = StatusRaw.objects.filter(monitor=monitor.keyname, timestamp__gte=now1h)

        bargraph = {}
        for i in range(60):
            bargraph[i] = {}
            bargraph[i]["OK"] = 0
            bargraph[i]["KO"] = 0
            bargraph[i]["ratio"] = -1
            bargraph[i]["tooltip"] = "NA"
            bargraph[i]["color"] = "grey"
            # x, width

        for raw in raws:
            diff = now - raw.timestamp
            diff_seconds = diff.seconds
            minute = int(diff_seconds / 60)
            if minute < 0 or minute > 59:
                continue
            if raw.status:
                bargraph[minute]["OK"] += 1
            else:
                bargraph[minute]["KO"] += 1

        # compute & pretty
        for i in range(60):
            #   ratio
            d = bargraph[i]["OK"] + bargraph[i]["KO"]
            if d > 0:
                bargraph[i]["ratio"] = bargraph[i]["OK"] / d
            else:
                bargraph[i]["ratio"] = -1

            # tootlip
            tooltip_time = (now_local - timedelta(minutes=i)).strftime("%H:%M")
            if bargraph[i]["ratio"] >= 1:
                tooltip_ratio = "100 %"
            elif bargraph[i]["ratio"] >= 0:
                tooltip_ratio = f"{100 * bargraph[i]['ratio']:.0f} %"
            else:
                tooltip_ratio = "NA"
            bargraph[i]["tooltip"] = f"{tooltip_time} - {tooltip_ratio}"

            #   x
            bargraph[i]["x"] = (59 - i) * WIDTH
            bargraph[i]["width"] = WIDTH - 3

            #   color
            color = "grey"
            if bargraph[i]["ratio"] >= 1:
                color = "green100"
            if bargraph[i]["ratio"] > 0:
                color = "green"
            else:
                color = "red"

            # if bargraph[i]['ratio'] >= 1:
            #     color = 'green100'
            # elif bargraph[i]['ratio'] > 0.99:
            #     color = 'green'
            # elif bargraph[i]['ratio'] > 0.98:
            #     color = 'orange'
            # elif bargraph[i]['ratio'] >= 0:
            #     color = 'red'
            bargraph[i]["color"] = color

        monitor.hourly_bargraph = bargraph

    return monitors


# -----------------------------------------------
# daily
# -----------------------------------------------
def compute_daily_ratio(monitors=None):
    """
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    """
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(hours=24)
    samples = StatusSampleHour.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for sample in samples:
        if sample.monitor not in table:
            table[sample.monitor] = {}
            table[sample.monitor]["OK"] = 0
            table[sample.monitor]["KO"] = 0
        if sample.count_ok:
            table[sample.monitor]["OK"] += sample.count_ok
        if sample.count_ko:
            table[sample.monitor]["KO"] += sample.count_ko

    #   compute ratio i=hour
    for i, counters in table.items():
        d = table[i]["OK"] + table[i]["KO"]
        if d > 0:
            table[i]["ratio"] = table[i]["OK"] / d
        else:
            table[i]["ratio"] = -1

    # ratio & pretty
    for monitor in monitors:
        monitor.daily_ratio = -1
        monitor.daily_ratio_pretty = "NA"

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]["ratio"]

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100 * ratio:.2f} %"
        else:
            pretty = "NA"

        monitor.daily_ratio = ratio
        monitor.daily_ratio_pretty = pretty

    return


def compute_daily_bargraph(monitors=None):
    """
    out:  []  'hour':int(0-23), 'ok':int, 'ko':int, 'ratio':float|-1
    """

    WIDTH = 30

    if not monitors:
        monitors = get_monitors()

    for monitor in monitors:
        now = timezone.now()  # django aware (UTC with TZ convert for rendering)
        # now24h = now - timedelta(hours=24)

        first_hour = (
            now
            - timedelta(minutes=now.minute)
            - timedelta(seconds=now.second)
            - timedelta(microseconds=now.microsecond)
            - timedelta(hours=24)
        )

        tmp = datetime.now()
        now_local = (
            tmp
            - timedelta(minutes=tmp.minute)
            - timedelta(seconds=tmp.second)
            - timedelta(microseconds=tmp.microsecond)
        )

        samples = StatusSampleHour.objects.filter(monitor=monitor.keyname, timestamp__gt=first_hour)

        bargraph = {}
        for i in range(24):
            bargraph[i] = {}
            bargraph[i]["OK"] = 0
            bargraph[i]["KO"] = 0
            bargraph[i]["ratio"] = -1
            bargraph[i]["tooltip"] = "NA"
            bargraph[i]["color"] = "grey"
            # x, width

        for sample in samples:
            diff = first_hour - sample.timestamp
            diff_seconds = diff.seconds
            hour = int(diff_seconds / 3600)

            if hour < 0 or hour > 23:
                continue
            if sample.count_ok:
                bargraph[hour]["OK"] += sample.count_ok
            if sample.count_ko:
                bargraph[hour]["KO"] += sample.count_ko

        # compute & pretty
        for i in range(24):
            #   ratio
            evtcount = bargraph[i]["OK"] + bargraph[i]["KO"]
            if evtcount > 0:
                bargraph[i]["ratio"] = bargraph[i]["OK"] / evtcount
            else:
                bargraph[i]["ratio"] = -1

            # tootlip
            tooltip_time = (now_local - timedelta(hours=i)).strftime("%H:%M")
            if bargraph[i]["ratio"] >= 1:
                tooltip_ratio = "100 %"
            elif bargraph[i]["ratio"] >= 0:
                tooltip_ratio = f"{100 * bargraph[i]['ratio']:.2f} %"
            else:
                tooltip_ratio = "NA"
            bargraph[i]["tooltip"] = f"{tooltip_time} - {tooltip_ratio} - {evtcount} evt"

            #   x
            bargraph[i]["x"] = (23 - i) * WIDTH
            bargraph[i]["width"] = WIDTH - 3

            #   color
            color = "grey"
            if bargraph[i]["ratio"] >= 1:
                color = "green100"
            elif bargraph[i]["ratio"] > 0.99:
                color = "green"
            elif bargraph[i]["ratio"] > 0.98:
                color = "orange"
            elif bargraph[i]["ratio"] >= 0:
                color = "red"
            bargraph[i]["color"] = color

        monitor.daily_bargraph = bargraph

    return monitors


# -----------------------------------------------
# monthly / 31d
# -----------------------------------------------
def compute_monthly_ratio(monitors=None):
    """
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    """
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(days=31)
    samples = StatusSampleDay.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for sample in samples:
        if sample.monitor not in table:
            table[sample.monitor] = {}
            table[sample.monitor]["OK"] = 0
            table[sample.monitor]["KO"] = 0
        if sample.count_ok:
            table[sample.monitor]["OK"] += sample.count_ok
        if sample.count_ko:
            table[sample.monitor]["KO"] += sample.count_ko

    #   compute ratio
    for monitor, counters in table.items():
        d = table[monitor]["OK"] + table[monitor]["KO"]
        if d > 0:
            table[monitor]["ratio"] = table[monitor]["OK"] / d
        else:
            table[monitor]["ratio"] = -1

    # ratio & pretty
    for monitor in monitors:
        monitor.monthly_ratio = -1
        monitor.monthly_ratio_pretty = "NA"

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]["ratio"]

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100 * ratio:.2f} %"
        else:
            pretty = "NA"

        monitor.monthly_ratio = ratio
        monitor.monthly_ratio_pretty = pretty

    return


def compute_monthly_bargraph(monitors=None):
    """
    out:  []  'day':int(0-30), 'ok':int, 'ko':int, 'ratio':float|-1
    """

    WIDTH = 26

    if not monitors:
        monitors = get_monitors()

    for monitor in monitors:
        now = timezone.now()  # django aware (UTC with TZ convert for rendering)

        first_day = (
            now
            - timedelta(days=31)
            - timedelta(hours=now.hour)
            - timedelta(minutes=now.minute)
            - timedelta(seconds=now.second)
            - timedelta(microseconds=now.microsecond)
        )

        tmp = datetime.now()
        now_local = (
            tmp
            - timedelta(hours=tmp.hour)
            - timedelta(minutes=tmp.minute)
            - timedelta(seconds=tmp.second)
            - timedelta(microseconds=tmp.microsecond)
        )

        samples = StatusSampleDay.objects.filter(monitor=monitor.keyname, timestamp__gt=first_day)

        bargraph = {}
        for i in range(31):
            bargraph[i] = {}
            bargraph[i]["OK"] = 0
            bargraph[i]["KO"] = 0
            bargraph[i]["ratio"] = -1
            bargraph[i]["tooltip"] = "NA"
            bargraph[i]["color"] = "grey"
            # x, width

        for sample in samples:
            diff = sample.timestamp - first_day
            day = 31 - diff.days

            if day < 0 or day > 30:
                continue
            if sample.count_ok:
                bargraph[day]["OK"] += sample.count_ok
            if sample.count_ko:
                bargraph[day]["KO"] += sample.count_ko

        # compute & pretty
        for i in range(31):
            #   ratio
            evtcount = bargraph[i]["OK"] + bargraph[i]["KO"]
            if evtcount > 0:
                bargraph[i]["ratio"] = bargraph[i]["OK"] / evtcount
            else:
                bargraph[i]["ratio"] = -1

            # tootlip
            tooltip_time = (now_local - timedelta(days=i)).strftime("%Y-%m-%d")
            if bargraph[i]["ratio"] >= 1:
                tooltip_ratio = "100 %"
            elif bargraph[i]["ratio"] >= 0:
                tooltip_ratio = f"{100 * bargraph[i]['ratio']:.2f} %"
            else:
                tooltip_ratio = "NA"
            bargraph[i]["tooltip"] = f"{tooltip_time} - {tooltip_ratio} - {evtcount} evt"

            #   x
            bargraph[i]["x"] = (30 - i) * WIDTH
            bargraph[i]["width"] = WIDTH - 3

            #   color
            color = "grey"
            if bargraph[i]["ratio"] >= 1:
                color = "green100"
            elif bargraph[i]["ratio"] > 0.99:
                color = "green"
            elif bargraph[i]["ratio"] > 0.98:
                color = "orange"
            elif bargraph[i]["ratio"] >= 0:
                color = "red"
            bargraph[i]["color"] = color

        monitor.monthly_bargraph = bargraph

    return monitors


# -----------------------------------------------
# quaterly - 90D
# -----------------------------------------------
def compute_quaterly_ratio(monitors=None):
    """
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    """
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(days=90)
    samples = StatusSampleDay.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for sample in samples:
        if sample.monitor not in table:
            table[sample.monitor] = {}
            table[sample.monitor]["OK"] = 0
            table[sample.monitor]["KO"] = 0
        if sample.count_ok:
            table[sample.monitor]["OK"] += sample.count_ok
        if sample.count_ko:
            table[sample.monitor]["KO"] += sample.count_ko

    #   compute ratio
    for monitor, counters in table.items():
        d = table[monitor]["OK"] + table[monitor]["KO"]
        if d > 0:
            table[monitor]["ratio"] = table[monitor]["OK"] / d
        else:
            table[monitor]["ratio"] = -1

    # ratio & pretty
    for monitor in monitors:
        monitor.quaterly_ratio = -1
        monitor.quaterly_ratio_pretty = "NA"

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]["ratio"]

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100 * ratio:.2f} %"
        else:
            pretty = "NA"

        monitor.quaterly_ratio = ratio
        monitor.quaterly_ratio_pretty = pretty

    return


def compute_quaterly_bargraph(monitors=None):
    """
    out:  []  'day':int(0-89), 'ok':int, 'ko':int, 'ratio':float|-1
    """

    WIDTH = 10

    if not monitors:
        monitors = get_monitors()

    for monitor in monitors:
        now = timezone.now()  # django aware (UTC with TZ convert for rendering)

        first_day = (
            now
            - timedelta(days=90)
            - timedelta(hours=now.hour)
            - timedelta(minutes=now.minute)
            - timedelta(seconds=now.second)
            - timedelta(microseconds=now.microsecond)
        )

        tmp = datetime.now()
        now_local = (
            tmp
            - timedelta(hours=tmp.hour)
            - timedelta(minutes=tmp.minute)
            - timedelta(seconds=tmp.second)
            - timedelta(microseconds=tmp.microsecond)
        )

        samples = StatusSampleDay.objects.filter(monitor=monitor.keyname, timestamp__gt=first_day)

        bargraph = {}
        for i in range(90):
            bargraph[i] = {}
            bargraph[i]["OK"] = 0
            bargraph[i]["KO"] = 0
            bargraph[i]["ratio"] = -1
            bargraph[i]["tooltip"] = "NA"
            bargraph[i]["color"] = "grey"
            # x, width

        for sample in samples:
            diff = sample.timestamp - first_day
            day = 90 - diff.days

            if day < 0 or day > 89:
                continue
            if sample.count_ok:
                bargraph[day]["OK"] += sample.count_ok
            if sample.count_ko:
                bargraph[day]["KO"] += sample.count_ko

        # compute & pretty
        for i in range(90):
            #   ratio
            evtcount = bargraph[i]["OK"] + bargraph[i]["KO"]
            if evtcount > 0:
                bargraph[i]["ratio"] = bargraph[i]["OK"] / evtcount
            else:
                bargraph[i]["ratio"] = -1

            # tootlip
            tooltip_time = (now_local - timedelta(days=i)).strftime("%Y-%m-%d")
            if bargraph[i]["ratio"] >= 1:
                tooltip_ratio = "100 %"
            elif bargraph[i]["ratio"] >= 0:
                tooltip_ratio = f"{100 * bargraph[i]['ratio']:.2f} %"
            else:
                tooltip_ratio = "NA"
            bargraph[i]["tooltip"] = f"{tooltip_time} - {tooltip_ratio} - {evtcount} evt"

            #   x
            bargraph[i]["x"] = (89 - i) * WIDTH
            bargraph[i]["width"] = WIDTH - 3

            #   color
            color = "grey"
            if bargraph[i]["ratio"] >= 1:
                color = "green100"
            elif bargraph[i]["ratio"] > 0.99:
                color = "green"
            elif bargraph[i]["ratio"] > 0.98:
                color = "orange"
            elif bargraph[i]["ratio"] >= 0:
                color = "red"
            bargraph[i]["color"] = color

        monitor.quaterly_bargraph = bargraph

    return monitors


# -----------------------------------------------
# yearly - 365D
# -----------------------------------------------
def compute_yearly_ratio(monitors=None):
    """
    out:  dict {} : monitor_name => {'OK':int, 'KO':int, 'ratio':int}
    """
    if not monitors:
        monitors = get_monitors()

    delta = timezone.now() - timedelta(days=365)
    samples = StatusSampleDay.objects.filter(timestamp__gte=delta)

    table = {}
    #   compute to buckets
    for sample in samples:
        if sample.monitor not in table:
            table[sample.monitor] = {}
            table[sample.monitor]["OK"] = 0
            table[sample.monitor]["KO"] = 0
        if sample.count_ok:
            table[sample.monitor]["OK"] += sample.count_ok
        if sample.count_ko:
            table[sample.monitor]["KO"] += sample.count_ko

    #   compute ratio
    for monitor, counters in table.items():
        d = table[monitor]["OK"] + table[monitor]["KO"]
        if d > 0:
            table[monitor]["ratio"] = table[monitor]["OK"] / d
        else:
            table[monitor]["ratio"] = -1

    # ratio & pretty
    for monitor in monitors:
        monitor.yearly_ratio = -1
        monitor.yearly_ratio_pretty = "NA"

        if monitor.keyname not in table:
            continue

        ratio = table[monitor.keyname]["ratio"]

        if ratio >= 1:
            pretty = "100 %"
        elif ratio >= 0:
            pretty = f"{100 * ratio:.2f} %"
        else:
            pretty = "NA"

        monitor.yearly_ratio = ratio
        monitor.yearly_ratio_pretty = pretty

    return


def compute_yearly_bargraph(monitors=None):
    pass


# -----------------------------------------------
# sample RAW to HOUR
# -----------------------------------------------
def sample_hour(monitors=None, start_date=None, end_date=None):

    if not monitors:
        monitors = get_monitors()

    if not start_date:
        start_date = timezone.now() - timedelta(hours=1)

    if not end_date:
        end_date = timezone.now()

    # get first/last timestamp
    first_hour = (
        start_date
        - timedelta(minutes=start_date.minute)
        - timedelta(seconds=start_date.second)
        - timedelta(microseconds=start_date.microsecond)
    )
    last_hour = (
        end_date
        - timedelta(minutes=end_date.minute)
        - timedelta(seconds=end_date.second)
        - timedelta(microseconds=end_date.microsecond)
    )

    current_hour = first_hour
    done = False
    total = 0  # samples created/updated
    while not done:
        next_hour = current_hour + timedelta(hours=1)

        for monitor in monitors:
            count_ok = StatusRaw.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_hour,
                timestamp__lt=next_hour,
                status=True,
            ).count()

            count_ko = StatusRaw.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_hour,
                timestamp__lt=next_hour,
                status=False,
            ).count()

            # delete previous entry
            StatusSampleHour.objects.filter(
                monitor=monitor.keyname, timestamp__gte=current_hour, timestamp__lt=next_hour
            ).delete()

            # create new entry
            obj = StatusSampleHour()
            obj.timestamp = current_hour
            obj.monitor = monitor.keyname
            obj.count_ok = count_ok
            obj.count_ko = count_ko
            obj.save()

            total += 1

        current_hour += timedelta(hours=1)

        if current_hour > last_hour:
            done = True

    return total


# -----------------------------------------------
# sample HOUR to DAY
# -----------------------------------------------
def sample_day(monitors=None, start_date=None, end_date=None):

    if not monitors:
        monitors = get_monitors()

    if not start_date:
        start_date = timezone.now() - timedelta(days=1)

    if not end_date:
        end_date = timezone.now()

    # get first/last timestamp
    first_day = (
        start_date
        - timedelta(hours=start_date.hour)
        - timedelta(minutes=start_date.minute)
        - timedelta(seconds=start_date.second)
        - timedelta(microseconds=start_date.microsecond)
    )
    last_day = (
        end_date
        - timedelta(hours=end_date.hour)
        - timedelta(minutes=end_date.minute)
        - timedelta(seconds=end_date.second)
        - timedelta(microseconds=end_date.microsecond)
    )

    current_day = first_day
    done = False
    total = 0  # samples created/updated
    while not done:
        next_day = current_day + timedelta(days=1)

        for monitor in monitors:
            tmp = StatusSampleHour.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_day,
                timestamp__lt=next_day,
            ).aggregate(Sum("count_ok"))
            count_ok = tmp["count_ok__sum"]

            tmp = StatusSampleHour.objects.filter(
                monitor=monitor.keyname,
                timestamp__gte=current_day,
                timestamp__lt=next_day,
            ).aggregate(Sum("count_ko"))
            count_ko = tmp["count_ko__sum"]

            # delete previous entry
            StatusSampleDay.objects.filter(
                monitor=monitor.keyname, timestamp__gte=current_day, timestamp__lt=next_day
            ).delete()

            # create new entry
            obj = StatusSampleDay()
            obj.timestamp = current_day
            obj.monitor = monitor.keyname
            obj.count_ok = count_ok
            obj.count_ko = count_ko
            obj.save()

            total += 1

        current_day += timedelta(days=1)

        if current_day > last_day:
            done = True

    return total


# -----------------------------------------------
# cleanup_raw per monitor
# -----------------------------------------------
def cleanup_raw(monitors=None):

    delete_all = False
    if not monitors:
        monitors = get_monitors()
        delete_all = True

    keepdays = int(get_configuration("status", "STATUS_RAW_KEEP_DAYS"))

    # delete before this timestamp
    timestamp = timezone.now() - timedelta(days=keepdays)

    # catch all if no monitors provided
    if delete_all:
        StatusRaw.objects.filter(timestamp__lt=timestamp).delete()

    else:
        for monitor in monitors:
            StatusRaw.objects.filter(monitor=monitor.keyname, timestamp__lt=timestamp).delete()


# -----------------------------------------------
# cleanup_hour per monitor
# -----------------------------------------------
def cleanup_hour(monitors=None):

    delete_all = False
    if not monitors:
        monitors = get_monitors()
        delete_all = True

    keepdays = int(get_configuration("status", "STATUS_SAMPLE_HOUR_KEEP_DAYS"))

    # delete before this timestamp
    timestamp = timezone.now() - timedelta(days=keepdays)

    # catch all if no monitors provided
    if delete_all:
        StatusSampleHour.objects.filter(timestamp__lt=timestamp).delete()

    else:
        for monitor in monitors:
            StatusSampleHour.objects.filter(
                monitor=monitor.keyname, timestamp__lt=timestamp
            ).delete()


# --------------------------------------------------------
# CLASS Monitor
# --------------------------------------------------------
class Monitor:
    def __init__(self, instance=None):

        self.keyname = ""
        self.displayname = ""
        self.is_enabled = True
        self.type = None

        self.app = ""
        self.timeout = 2000
        self.schedule = 5
        self.pattern = ""

        # state OK/KO/NA [DOWNTIME]
        self.live_status = "NA"
        # hourly
        self.hourly_ratio = None
        self.hourly_ratio_pretty = None
        self.hourly_bargraph = None  #  dict minute => bar ; bar = dict {x/ratio/count_ok/color/...}
        # daily
        self.daily_ratio = None
        self.daily_ratio_pretty = None
        self.daily_bargraph = None  #  dict hour => bar ; bar = dict {x/ratio/count_ok/color/...}
        # monthly
        self.monthly_ratio = None
        self.monthly_ratio_pretty = None
        self.monthly_bargraph = None  #  dict day => bar ; bar = dict {x/ratio/count_ok/color/...}
        # quaterly
        self.quaterly_ratio = None
        self.quaterly_ratio_pretty = None
        self.quaterly_bargraph = None  #  dict XXX => bar ; bar = dict {x/ratio/count_ok/color/...}
        # yearly
        self.yearly_ratio = None
        self.yearly_ratio_pretty = None
        self.yearly_bargraph = None  #  dict XXX => bar ; bar = dict {x/ratio/count_ok/color/...}

        #   run
        self.run_done = False
        self.run_error = None
        self.run_duration = None

        # existing from DB
        if not instance:
            return

        # populate with DB instance
        self.keyname = instance.keyname
        self.displayname = instance.displayname
        self.is_enabled = instance.is_enabled

        try:
            self.app = instance.get_attribute_first("app")
        except Exception:
            self.app = ""

        try:
            self.timeout = int(instance.get_attribute_first("timeout"))
        except Exception:
            self.timeout = 2000

        try:
            self.schedule = int(instance.get_attribute_first("schedule"))
        except Exception:
            self.schedule = 5

        try:
            self.target = instance.get_attribute_first("target")
        except Exception:
            self.target = ""

        try:
            self.pattern = instance.get_attribute_first("pattern")
        except Exception:
            self.pattern = ""

    def __str__(self):
        return f"{self.displayname}"

    def run(self):
        # overload in herited MonitorXXXX
        self.run_done = False
        self.run_duration = 0
        self.run_error = "not implemented"

    def update_last_state(self, second=300):
        self.last_state = "OK"


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


class MonitorHTTP(Monitor):
    def __init__(self, instance=None):
        super().__init__(instance=instance)

        self.type = "HTTP"

        self.http_code = 200
        self.http_host = ""
        self.http_ssl_verify = True
        self.http_allow_redirect = True
        #   run
        self.run_http_code = None

        try:
            self.http_code = int(instance.get_attribute_first("http_code"))
        except Exception:
            self.http_code = 200

    def run(self):
        super().run()

        self.run_done = True
        self.run_duration = 0
        self.run_error = ""

        # my_http_proxy  = c.get('http_proxy',"")
        # my_https_proxy = c.get('https_proxy',my_http_proxy)
        # # default : use env proxies
        # # if "http_proxy:noenv" remove env proxies
        # # else use specified proxies
        # proxies = {}
        # if my_http_proxy != "":
        #     proxies["http"] = my_http_proxy
        # if my_https_proxy != "":
        #     proxies["https"] = my_https_proxy
        # session = requests.Session()
        # if my_http_proxy =="noenv":
        #     session.trust_env = False
        #     proxies = {}
        # ##print("\n---\n",proxies)

        headers = {}
        if self.http_host != "":
            headers["Host"] = self.http_host

        start_time = time.time()

        try:
            resp = requests.get(
                self.target,
                headers=headers,
                timeout=int(self.timeout / 1000),
                verify=self.http_ssl_verify,
                # proxies=proxies,
                allow_redirects=self.http_allow_redirect,
                # auth=(self.http_username, self.http_password),
            )
        except Exception as e:
            self.run_error = e
            return

        # usec
        self.run_duration = int(1000000 * (time.time() - start_time))

        # http_code : resp.status_code
        self.run_http_code = int(resp.status_code)
        if self.run_http_code != self.http_code:
            self.run_error = f"bad http_code {resp.status_code}"
            return

        # check for pattern, in page content AND in header
        if self.pattern and len(self.pattern) > 0:
            fulltext = ""
            for k, v in resp.headers.items():
                line = f"{k}: {v}\n"
                fulltext += line
            fulltext += resp.text

            # acceptance pattern
            if self.pattern[0] != "!":
                mysearch = re.search(self.pattern, fulltext)
                if not mysearch:
                    self.run_error = f"expected pattern not found ({self.pattern})"
                    return
            # rejection pattern
            else:
                mysearch = re.search(self.pattern[1:], fulltext)
                if mysearch:
                    self.run_error = f"reject pattern found ({self.pattern})"
                    return
