# _list_action.py
#
# Project: AutoArchive
# License: GNU GPLv3
#
# Copyright (C) 2003 - 2023 Róbert Čerňanský



from datetime import date

from AutoArchive._infrastructure.configuration import Options, OptionsUtils
from AutoArchive._infrastructure.ui import VerbosityLevels, DisplayField, FieldStretchiness, MultiFieldLine
from .action_utils import ActionUtils
from .._archive_info import _BackupLevelRestartReasons
from ..._action.action_result import ActionResult
from ..._action.action import Action



class ListAction(Action):

    def __init__(self, actionUtils: ActionUtils, selectedArchiveSpecs):
        super().__init__(actionUtils.actionResult)

        self.__actionUtils = actionUtils
        self.__selectedArchiveSpecs = selectedArchiveSpecs

        self.__actionResult = actionUtils.actionResult
        self.__archiving = actionUtils.archiving
        self.__componentUi = actionUtils.componentUi



    def executeAction_(self) -> bool:
        archiveSpecsForProcessing = self.__actionUtils.getArchiveSpecsForProcessing(self.__selectedArchiveSpecs)
        if archiveSpecsForProcessing is None:
            return False

        orphanedArchives = frozenset(self.__actionUtils.getOrphanedArchives(archiveSpecsForProcessing))

        for archiveSpecInfo in archiveSpecsForProcessing:
            processingArchSpec = archiveSpecInfo.name or archiveSpecInfo.path
            self.__componentUi.setProcessingArchSpec(processingArchSpec)

            # if the archive is orphaned do not even try to get information about it because it most certainly will
            # fail and an error will be shown which is undesirable
            if processingArchSpec in orphanedArchives:
                continue

            archiveInfo = self.__archiving.getArchiveInfo(archiveSpecInfo.path)
            if archiveInfo is None:
                self.__actionResult.set(ActionResult.ActionResults.Failed)
                continue

            if self.__componentUi.verbosity == VerbosityLevels.Verbose:
                self.__showVerboseArchiveInfo(archiveInfo)
            else:
                self.__showStandardArchiveInfo(archiveInfo)
        self.__componentUi.setProcessingArchSpec(None)

        archiveSpecsNames = {spec.name for spec in archiveSpecsForProcessing}
        for orphanedArchiveName in orphanedArchives:
            self.__componentUi.setProcessingArchSpec(orphanedArchiveName)

            # if user passed some arguments (selected some archives) and option ALL is not enabled then we will going
            # to list only those orphaned archives which were specified (as arguments)
            if (len(self.__selectedArchiveSpecs) > 0 and not self.__actionUtils.configuration[Options.ALL]) and \
               (orphanedArchiveName not in archiveSpecsNames):
                continue

            archiveInfo = self.__archiving.getStoredArchiveInfo(orphanedArchiveName)

            if self.__componentUi.verbosity == VerbosityLevels.Verbose:
                self.__showVerboseArchiveInfo(archiveInfo, True)
            else:
                self.__showStandardArchiveInfo(archiveInfo, True)
        self.__componentUi.setProcessingArchSpec(None)

        return True



    def __showVerboseArchiveInfo(self, archiveInfo, orphaned = False):
        archiveInfoMsg = str.format("Name: {}\n", self.__bracket(archiveInfo.name, orphaned))
        archiveInfoMsg += str.format("Root: {}\n", self.__question(archiveInfo.path, True))
        archiveInfoMsg += str.format("Archiver type: {}\n", OptionsUtils.archiverTypeToStr(archiveInfo.archiverType))
        archiveInfoMsg += str.format("Destination directory: {}\n", archiveInfo.destDir)
        archiveInfoMsg += str.format("Current backup level/next/max.: {}\n", self.__formatLevelsString(archiveInfo))
        archiveInfoMsg += str.format(
            "Target backup level for non-full restart: {}\n",
            self.__bracket(self.__question(archiveInfo.restartLevel, archiveInfo.incremental is not None),
                           not archiveInfo.incremental or not archiveInfo.restarting))
        archiveInfoMsg += str.format(
            "Upcoming restart reason: {}\n",
            self.__bracket(self.__question(self.__reasonToStr(archiveInfo.restartReason),
                                           archiveInfo.incremental is not None),
                           not archiveInfo.incremental or not archiveInfo.restarting))
        archiveInfoMsg += str.format("Restart count/max.: {}/{}\n",
                                     self.__bracket(self.__dash(archiveInfo.restartCount),
                                                    not archiveInfo.incremental or not archiveInfo.restarting),
                                     self.__bracket(self.__dash(archiveInfo.fullRestartAfterCount),
                                                    not archiveInfo.incremental or not archiveInfo.restarting))
        age = (date.today() - archiveInfo.lastRestart).days if archiveInfo.lastRestart is not None else None
        archiveInfoMsg += str.format("Days since last restart/max.: {}/{}\n",
                                     self.__bracket(self.__dash(age), not archiveInfo.incremental or
                                                                      not archiveInfo.restarting),
                                     self.__bracket(self.__dash(archiveInfo.restartAfterAge),
                                                    not archiveInfo.incremental or not archiveInfo.restarting))
        age = (date.today() - archiveInfo.lastFullRestart).days if archiveInfo.lastFullRestart is not None else None
        archiveInfoMsg += str.format("Days since last full restart/max.: {}/{}\n",
                                     self.__bracket(self.__dash(age), not archiveInfo.incremental or
                                                                      not archiveInfo.restarting),
                                     self.__bracket(self.__dash(archiveInfo.fullRestartAfterAge),
                                                    not archiveInfo.incremental or not archiveInfo.restarting))
        self.__componentUi.showVerbose(archiveInfoMsg)



    def __showStandardArchiveInfo(self, archiveInfo, orphaned = False):
        name = DisplayField(self.__bracket(archiveInfo.name, orphaned), 0.087, FieldStretchiness.Medium)
        path = DisplayField(self.__question(archiveInfo.path, True), 0.447, FieldStretchiness.Normal)
        destDir = DisplayField(archiveInfo.destDir, 0.448, FieldStretchiness.Normal)
        levels = DisplayField(self.__formatLevelsString(archiveInfo), 0.015, FieldStretchiness.Low)

        self.__componentUi.presentMultiFieldLine(MultiFieldLine([name, path, destDir, levels], 1000))



    def __formatLevelsString(self, archiveInfo):
        levels = str.format(
            "{}/{}/{}",
            self.__bracket(self.__dash(archiveInfo.backupLevel), not archiveInfo.incremental),
            self.__bracket(self.__question(archiveInfo.nextBackupLevel,
                                           archiveInfo.incremental is not None and archiveInfo.backupLevel is not None),
                           not archiveInfo.incremental),
            self.__bracket(self.__dash(archiveInfo.restartAfterLevel), not archiveInfo.incremental or
                                                                       not archiveInfo.restarting))
        return levels



    @staticmethod
    def __bracket(token, condition):
        leftBracket = ""
        rightBracket = ""
        if condition and token is not None:
            leftBracket = "["
            rightBracket = "]"
        return str.format("{leftBracket}{token}{rightBracket}", leftBracket = leftBracket, token = token,
                          rightBracket = rightBracket)



    @staticmethod
    def __dash(value):
        if value is None:
            return "-"
        else:
            return value



    @classmethod
    def __question(cls, value, condition):
        if condition and value is None:
            return "?"
        else:
            return cls.__dash(value)



    @classmethod
    def __reasonToStr(cls, reason):
        if reason is _BackupLevelRestartReasons.NoRestart:
            reasonStr = "No restart scheduled for the next backup."
        elif reason is _BackupLevelRestartReasons.RestartCountLimitReached:
            reasonStr = "Maximal restart count reached."
        elif reason is _BackupLevelRestartReasons.LastFullRestartAgeLimitReached:
            reasonStr = "Maximal age without full restart reached."
        elif reason is _BackupLevelRestartReasons.BackupLevelLimitReached:
            reasonStr = "Maximal backup level reached."
        elif reason is _BackupLevelRestartReasons.LastRestartAgeLimitReached:
            reasonStr = "Maximal age without a restart reached."
        elif reason is None:
            reasonStr = None
        else:
            reasonStr = "Unknown reason."
        return reasonStr
