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

from production.repository import ProductionRepository
from production.model import PicklistCleanerManagerConfig

logger = getLogger("PicklistDatabaseManager")


class PicklistCleanerManager(object):
    def __init__(
        self,                   # type: PicklistCleanerManager
        production_repository,  # type: ProductionRepository
        config,                 # type: PicklistCleanerManagerConfig
    ):
        # type: (...) -> None

        super(PicklistCleanerManager, self).__init__()
        self.production_repository = production_repository
        self.start_time = config.start_time
        self.end_time = config.end_time
        self.days_to_keep_data = config.days_to_keep_data

        self.condition = Condition()
        self.running = False
        self.minutes_to_call = 30
        self.has_already_applied_cleaning = False

    def handle_clean_databases(
        self,   # type: PicklistCleanerManager
    ):
        # type: (...) -> None

        logger.info("Start cleaning picklist databases")

        try:
            self.production_repository.delete_outdated_pick_list_orders(range_days=self.days_to_keep_data)
            vacuum_size_info = self.production_repository.vacuum_database()
            self.has_already_applied_cleaning = True

            log_message = "Production db file sizes. OLD: {} bytes; NEW: {} bytes".format(
                vacuum_size_info.get("old_file_size"),
                vacuum_size_info.get("new_file_size"),
            )
            logger.info(log_message)
        except Exception as _:
            logger.exception(
                "Error cleaning picklist databases",
                exc_info=True,
            )

        logger.info("Picklist databases cleaned with success")

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

        if not self.running:
            self.running = True
            thread = Thread(target=self._cleaner)
            thread.name = "PicklistDatabaseManager"
            thread.daemon = True
            thread.start()

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

        if self.running:
            with self.condition:
                self.running = False
                self.condition.notify_all()

    def _cleaner(
        self,   # type: PicklistCleanerManager
    ):
        # type: (...) -> None

        self.handle_clean_databases()

        if self._clean_only_on_component_start():
            self.stop()
            return

        while self.running:
            if self.is_in_maintenance_window():
                self.handle_clean_databases()
            self._sleep()

    def _clean_only_on_component_start(
        self,   # type: PicklistCleanerManager
    ):
        # type: (...) -> bool

        return self.start_time is None or self.end_time is None

    def _sleep(
        self,  # type: PicklistCleanerManager
    ):
        # type: (...) -> None

        with self.condition:
            self.condition.wait(self.minutes_to_call * 60)

    def is_in_maintenance_window(
        self,        # type: PicklistCleanerManager
    ):
        # 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

