from abc import abstractmethod
from typing import Callable

from PyEmailerAJM.backend import AlertTypes
from PyEmailerAJM.continuous_monitor.backend.continuous_monitor_base import ContinuousMonitorBase
from PyEmailerAJM.msg import MsgFactory


class ContinuousMonitor(ContinuousMonitorBase):
    TITLE_STRING = " Watching for emails with alerts in {} folder ".center(100, '*')
    MSG_FACTORY_CLASS: MsgFactory = MsgFactory
    ALERT_CHECK_STR = "Checking for emails with an alert..."
    NO_ALERTS_STR = "No emails with an alert detected in {read_folder} ({num_snoozed} snoozed)."

    def GetMessages(self, folder_index=None):
        """
        :param folder_index: Index of the folder from which messages are retrieved. Defaults to None if not specified.
        :type folder_index: int, optional
        :return: A list of sorted and filtered message objects, each containing an alert.
        :rtype: list
        """
        msgs = super().GetMessages(folder_index)
        sorted_msgs = [
            self.__class__.MSG_FACTORY_CLASS.get_msg(x, logger=self.logger, snooze_checker=self.snooze_tracker) for x in
            msgs]
        alert_messages = [x for x in sorted_msgs if x is not None and x.msg_alert]
        return alert_messages

    def _set_args_for_endless_watch(self):
        """
        Sets specific arguments for the endless_watch process.

        :return: None
        :rtype: None
        """
        self.send_emails = False
        self.auto_send = False
        self.display_window = False

    @abstractmethod
    def _postprocess_alert(self, alert_level=None, **kwargs):
        ...

    def _process_no_alert(self, **kwargs):
        no_alert_str = kwargs.get('no_alert_string',
                                  self.__class__.NO_ALERTS_STR.format(read_folder=self.read_folder,
                                                                      num_snoozed=self.num_snoozed_msgs))
        self.logger.info(no_alert_str, print_msg=True)

    def _classify_and_process(self, **kwargs):
        if self.has_overdue:
            self._print_and_postprocess(AlertTypes.OVERDUE)

        elif self.has_warning:
            self._print_and_postprocess(AlertTypes.WARNING)

        elif self.has_critical_warning:
            self._print_and_postprocess(AlertTypes.CRITICAL_WARNING)

        else:
            self._process_no_alert(**kwargs)

    # TODO: make no_alerts_string property so there is more flexibility with format
    def check_for_alerts(self, **kwargs):
        """
        Checks for emails in the specified folder and identifies if there are any alerts. Alerts,
        if present, are categorized as overdue, warning, or critical warning, and are processed accordingly.
        Then logs the result of the check.

        :return: None
        :rtype: None

        """
        alert_check_string = kwargs.get('alert_check_string', self.__class__.ALERT_CHECK_STR)
        self.logger.info(alert_check_string, print_msg=True)
        self.refresh_messages()

        self._classify_and_process(**kwargs)

        self.snooze_tracker.snooze_msgs(self.all_messages)

    def endless_watch(self, stop_condition: Callable[[], bool] = None):
        if not self.dev_mode:
            self._set_args_for_endless_watch()

        stop_condition = stop_condition or (lambda: False)  # Default stop_condition
        email_dir_name = self.read_folder.name if self.read_folder else None

        self.logger.info(self.__class__.TITLE_STRING.format(email_dir_name), print_msg=True)

        while not stop_condition():
            try:
                self.check_for_alerts()
                self._was_refreshed = False
                self.sleep_timer.sleep_in_rounds()
            except KeyboardInterrupt:
                self.logger.error("KeyboardInterrupt detected, exiting program.")
                break


if __name__ == '__main__':
    ContinuousMonitor.MSG_FACTORY_CLASS.ALERT_SUBJECT_KEYWORDS = ['training']
    cm = ContinuousMonitor(False, False, dev_mode=False, show_warning_logs_in_console=True, )
    cm.endless_watch()
