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



""":class:`TestActions`."""



__all__ = ["TestActions"]



import itertools
import re
import unittest

from AutoArchive._application.archiving.archive_spec import ArchiveSpecInfo
from AutoArchive._application.archiving.archive_spec.tests import ArchiveSpecTestUtils
from AutoArchive._application.archiving.tests.archiving_test_utils import ArchivingTestUtils
from AutoArchive._application.archiving_application import ArchivingApplication
from AutoArchive._infrastructure.configuration import Options
from AutoArchive._infrastructure.configuration.tests import ConfigurationTestUtils
from AutoArchive._infrastructure.py_additions import event
from AutoArchive._infrastructure.ui import VerbosityLevels
from AutoArchive._services.archiver import ArchiverFeatures, BackupOperationErrors
from AutoArchive._services.archiver.tests import ArchiverTestUtils
from AutoArchive._ui.cmdline.tests.cmdline_ui_test_utils import CmdlineUiTestUtils
from AutoArchive.tests import ComponentTestUtils



class TestActions(unittest.TestCase):
    """Test of basic use cases of public API of :meth:`.ArchivingApplication` class."""

    @classmethod
    def setUpClass(cls):
        ArchivingTestUtils._setUpClassArchivingComponent()



    @classmethod
    def tearDownClass(cls):
        ArchivingTestUtils._tearDownClassArchivingComponent()



    def setUp(self):
        ArchivingTestUtils._setUpArchivingComponent()
        self.__cmdlineUiMock = None

        self.__archiverMock = ArchiverTestUtils.createArchiverMock()
        options = {Options.ARCHIVE_SPECS_DIR: ComponentTestUtils.getComponentTestContext().archiveSpecsDir}
        configurationMock = ConfigurationTestUtils.createConfigurationMock(options)
        self.__applicationContextMock = ArchivingTestUtils._setUpApplicationContextMock(
            configurationMock = configurationMock)
        self.__serviceAccessorMock = ArchivingTestUtils._setUpServiceAccessorMock()



    def tearDown(self):
        ArchivingTestUtils._tearDownArchivingComponent()



    def test_createAction(self):
        """Tests the create action.

        Sets up two archive specification files and passes them to tested method.  Checks that service method for
        backup creation was called two times."""

        selectedArchiveSpecs = (ArchiveSpecInfo("any name 1", ArchiveSpecTestUtils.makeArchiveSpecFile()),
                                ArchiveSpecInfo("any name 2", ArchiveSpecTestUtils.makeArchiveSpecFile()))

        archivingApplication = self.__setupAndCreateArchivingApplication()
        archivingApplication.executeCreateAction(selectedArchiveSpecs)

        self.assertEqual(len(selectedArchiveSpecs), self.__archiverMock.backupFiles.call_count)



    def test_createActionHandleResultWarning(self):
        """Tests the create action with warnings during backup operation.

        Sets up two archive specification files and passes them to tested method.  Setup archiver services to make
        an error that results to showing a Warning message during operation.  Setup UI to Verbose level.  Checks that
        final message matches the one which shall be shown when Warning has occurred during operation."""

        selectedArchiveSpecs = (ArchiveSpecInfo("any name 1", ArchiveSpecTestUtils.makeArchiveSpecFile()),
                                ArchiveSpecInfo("any name 2", ArchiveSpecTestUtils.makeArchiveSpecFile()))
        expectedMessageRegexp = "one or more warnings"

        archivingApplication = self.__setupAndCreateArchivingApplication(
            BackupOperationErrors.SocketIgnored, VerbosityLevels.Verbose)
        archivingApplication.executeCreateAction(selectedArchiveSpecs)

        verboseCallsArgs = (methodCall[1]
                            for methodCall in self.__cmdlineUiMock.method_calls
                            if methodCall[0] == "showVerbose")
        creationFailedCallsArgs = itertools.dropwhile(
            lambda callArg: re.search(expectedMessageRegexp, callArg[0], re.IGNORECASE) is None, verboseCallsArgs)

        self.assertIsNotNone(
            next(creationFailedCallsArgs, None), f"The message marching '{expectedMessageRegexp}' was not shown.")



    def test_createActionHandleResultError(self):
        """Tests the create action with error during backup operation.

        Sets up two archive specification files and passes them to tested method.  Setup archiver services to make
        an error that results to showing an Error message during operation.  Setup UI to Verbose level.  Checks that
        final message matches the one which shall be shown when Error has occurred during operation."""

        selectedArchiveSpecs = (ArchiveSpecInfo("any name 1", ArchiveSpecTestUtils.makeArchiveSpecFile()),
                                ArchiveSpecInfo("any name 2", ArchiveSpecTestUtils.makeArchiveSpecFile()))
        expectedMessageRegexp = "finished with error"

        archivingApplication = self.__setupAndCreateArchivingApplication(
            BackupOperationErrors.UnknownOsError, VerbosityLevels.Verbose)
        archivingApplication.executeCreateAction(selectedArchiveSpecs)

        verboseCallsArgs = (methodCall[1]
                            for methodCall in self.__cmdlineUiMock.method_calls
                            if methodCall[0] == "showVerbose")
        creationFailedCallsArgs = itertools.dropwhile(
            lambda callArg: re.search(expectedMessageRegexp, callArg[0], re.IGNORECASE) is None, verboseCallsArgs)

        self.assertIsNotNone(
            next(creationFailedCallsArgs, None), f"The message matching '{expectedMessageRegexp}' was not shown.")



    def test_listAction(self):
        """Tests the list action.

        Sets up two archive specification files and passes them to tested method.  Checks that UI method for showing
        lines was called two times."""

        selectedArchiveSpecs = (ArchiveSpecInfo("any name 1", ArchiveSpecTestUtils.makeArchiveSpecFile()),
                                ArchiveSpecInfo("any name 2", ArchiveSpecTestUtils.makeArchiveSpecFile()))

        self.__archiverMock.getStoredBackupIds.return_value = ()
        self.__archiverMock.getMaxBackupLevel.return_value = 3 # any int

        archivingApplication = self.__setupAndCreateArchivingApplication()
        archivingApplication.executeListAction(selectedArchiveSpecs)

        self.assertEqual(len(selectedArchiveSpecs), self.__cmdlineUiMock.presentMultiFieldLine.call_count)



    def test_purgeAction(self):
        """Tests the purge action.

        Sets up two archive info classes that represents non existing archives and passes them to tested method.
        Checks that service method for purging stored backup data was called for each archive and each service
        provider."""

        selectedArchiveSpecs = (ArchiveSpecInfo("test ID 1", ""),
                                ArchiveSpecInfo("test ID 2", ""))

        self.__archiverMock.getStoredBackupIds.return_value = (selectedArchiveSpecs[0].name, selectedArchiveSpecs[1].name)

        archivingApplication = self.__setupAndCreateArchivingApplication()
        archivingApplication.executePurgeAction(selectedArchiveSpecs)

        # check that purge was called on the service; for each selected archive ID twice because it is executed by
        # each service provider
        self.assertEqual(len(selectedArchiveSpecs) * 2, self.__archiverMock.purgeStoredBackupData.call_count)



    def __setupAndCreateArchivingApplication(
            self, backupOperationError: BackupOperationErrors = None, verbosity: VerbosityLevels = None):
        self.__archiverMock.fileAdd = event(lambda x: x)
        self.__cmdlineUiMock = CmdlineUiTestUtils.createCmdlineUiMock(verbosity)

        ArchivingTestUtils._setUpArchiverServices(self.__serviceAccessorMock, self.__archiverMock,
            backupOperationError = backupOperationError, supportedFeatures = frozenset({ArchiverFeatures.Incremental}))
        return ArchivingApplication(self.__cmdlineUiMock, self.__applicationContextMock, self.__serviceAccessorMock)
