from datetime import datetime
from logging import Logger
from threading import (
    Thread,
    Condition,
)

from mbcontextmessagehandler import MbContextMessageBus

from externals.repository import AlertsRepository
from services.model import Configs


class DatabaseManager(object):
    def __init__(
        self,               # type: DatabaseManager
        message_bus,        # type: MbContextMessageBus
        alerts_repository,  # type: AlertsRepository
        configs,            # type: Configs
        logger,             # type: Logger
    ):
        # type: (...) -> None

        self.message_bus = message_bus
        self.alerts_repository = alerts_repository
        self.start_time = configs.alerts.maintenance_window_start
        self.end_time = configs.alerts.maintenance_window_end
        self.days_to_keep_data = configs.alerts.days_to_keep_data
        self.logger = logger

        self.thread = None
        self.running = False
        self.sleep_condition = Condition()
        self.interval_in_minutes = 30
        self.has_already_applied_cleaning = False

    def start(
        self,  # type: DatabaseManager
    ):
        # type: (...) -> None

        if not self.running:
            self.running = True
            self.thread = Thread(
                target=self._clean_runner,
                name="CleanDatabases",
            )
            self.thread.daemon = True
            self.thread.start()

    def stop(
        self,   # type: DatabaseManager
    ):
        # type: (...) -> None

        if self.running:
            with self.sleep_condition:
                self.running = False
                self.sleep_condition.notify()

    def _clean_runner(
        self,   # type: DatabaseManager
    ):
        # type: (...) -> None

        self._handle_clean_databases()

        clean_only_on_component_start = self.start_time is None or self.end_time is None
        if clean_only_on_component_start:
            self.stop()
            return

        while self.running:
            if self._allowed_to_clean_databases():
                self._handle_clean_databases()

            with self.sleep_condition:
                self.sleep_condition.wait(self.interval_in_minutes * 60)

    def _handle_clean_databases(
        self,   # type: DatabaseManager
    ):
        # type: (...) -> None

        self.logger.info("Start cleaning databases")

        try:
            self.alerts_repository.delete_outdated_events(range_days=self.days_to_keep_data)
            self.alerts_repository.vacuum_database()
            self.has_already_applied_cleaning = True
        except Exception as _:
            self.logger.exception(
                "Error cleaning databases",
                exc_info=True,
            )

        self.logger.info("Databases cleaned with success")

    def _allowed_to_clean_databases(
        self,   # type: DatabaseManager
    ):
        # type: (...) -> bool

        now = datetime.now().time()
        is_inside_clean_window = self.start_time <= now <= self.end_time

        if self.has_already_applied_cleaning and not is_inside_clean_window:
            self.has_already_applied_cleaning = False

        if not self.has_already_applied_cleaning and is_inside_clean_window:
            return True

        return False
