"""
This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License,
or (at your option) any later version.


This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
See the GNU General Public License for more details.


You should have received a copy of the GNU General Public License
 along with this program.  If not, see <https://www.gnu.org/licenses/>.

Copyright © 2019 Cloud Linux Software Inc.

This software is also available under ImunifyAV commercial license,
see <https://www.imunify360.com/legal/eula>
"""
from enum import Enum


class MalwareScanType:
    """The type of a scan represents how and why it is executed"""

    #: .. deprecated:: <unknown> no longer used
    MALWARE_RESPONSE = "malware-response"
    #: A scan of arbitrary path or path pattern started by admin or user.
    ON_DEMAND = "on-demand"
    #: A batch of files scanned by resident mode AI-BOLIT.
    #:
    #: FIXME this scan type is also used as a default in
    #: :meth:`.Scanner._process_scan_task`
    #: Afaik, because of this, rescans triggered by ignore list removal,
    #: have this `type`. I'm not sure if there are more.
    REALTIME = "realtime"
    #: Used in :class:`.MalwareHistory` to indicate a manual action on a file,
    #: not directly caused by any scan.
    #:
    #: .. deprecated:: - FIXME this is not really a scan type...
    MANUAL = "manual"
    #: Similar to :attr:`MalwareScanType.USER`, but started from our cron job.
    #:
    #: See the `MALWARE_SCAN_SCHEDULE` config option for more info.
    #: Background scans are reported to ClickHouse even if there was
    #: no malware found.
    BACKGROUND = "background"
    #: Correlation server asked to rescan some files.
    RESCAN = "rescan"
    #: Rescan triggered by the agent when file is modified after scanning
    RESCAN_OUTDATED = "rescan-outdated"
    #: ModSec rule triggered a scan on file upload.
    #:
    #: .. deprecated:: 4.4.0
    #:     was used for ModSec upload scans before resident mode AI-BOLIT
    #:     (DEF-9201)
    MODSEC = "modsec"
    #: A scan of one user home directory.
    USER = "user"
    #: An attempt to find a clean version of a file in backups.
    RESTORE_FROM_BACKUP = "restore_from_backup"


AIBOLIT_SCAN_INTENSITY_KEY = {
    MalwareScanType.ON_DEMAND: "aibolit-on_demand",
    MalwareScanType.BACKGROUND: "aibolit-background",
    MalwareScanType.USER: "aibolit-user",
    MalwareScanType.RESCAN: "aibolit-rescan",
    MalwareScanType.RESCAN_OUTDATED: "aibolit-rescan",
    MalwareScanType.REALTIME: "aibolit-realtime",
    MalwareScanType.RESTORE_FROM_BACKUP: "aibolit-restore_from_backup",
}
APP_VERSION_DETECTOR_INTENSITY_KEY = "app_version_detector"
HSDB_MAKER_INTENSITY_KEY = "aibolit-hsdb_maker"
RESTORE_ORIGINAL_INTENSITY_KEY = "aibolit-restore_original"

PHP_PATH = "/opt/alt/php-internal/usr/bin/php"

SUCCESSFUL_EVENTS = (
    DELETED,
    FOUND,
    ADDED_TO_IGNORE,
    DELETED_FROM_IGNORE,
    SUBMITTED_FOR_ANALYSIS,
    CLEANUP_DONE,
    CLEANUP_REMOVED,
    RESTORED_ORIGINAL,
    # FIXME: not used. Should we save this event in history?
    RESTORE_FROM_BACKUP_STARTED,
    RESTORED_FROM_BACKUP,
    PENDING,
    FAILED_TO_SUBMIT,
) = (
    "deleted",
    "found",
    "added_to_ignore",
    "deleted_from_ignore",
    "submitted_for_analysis",
    "cleanup_done",
    "cleanup_removed",
    "restored_original",
    "restore_from_backup_started",
    "restored_from_backup",
    "pending",
    "failed_to_submit",
)

FAILED_EVENTS = (
    # FIXME: not used
    FAILED_TO_DELETE,
    # FIXME: not used
    FAILED_TO_RESTORE_FROM_BACKUP,
    FAILED_TO_IGNORE,
    FAILED_TO_DELETE_FROM_IGNORE,
    FAILED_TO_CLEANUP,
    FAILED_TO_RESTORE_ORIGINAL,
    UNABLE_TO_CLEANUP,
    FAILED_TO_STORE_ORIGINAL,
    REQUIRES_MYIMUNIFY_PROTECTION,
    NOT_EXIST,
) = (
    "failed_to_delete",
    "failed_to_restore_from_backup",
    "failed_to_ignore",
    "failed_to_delete_from_ignore",
    "failed_to_cleanup",
    "failed_to_restore_original",
    "unable_to_cleanup",
    "failed_to_store_original",
    "requires_myimunify_protection",
    "not_exist",
)


class MalwareEvent:
    def __init__(self, title):
        self.title = title

    @property
    def successful(self) -> bool:
        return self.title in SUCCESSFUL_EVENTS

    @property
    def malware_eliminated(self) -> bool:
        """Malware file was deleted or clean copy was restored"""
        return self.title in (DELETED, RESTORED_FROM_BACKUP)


class MalwareEventPostponed:
    def __init__(self, message, cause, initiator, post_action, action):
        self.message = message
        self.cause = cause
        self.initiator = initiator
        self.post_action = post_action
        self.action = action


class Hash:
    TYPES = WHITE, BLACK = "white", "black"

    IS_IGNORED = {BLACK: False, WHITE: True}


class MalwareHitStatus:
    """
    The current state of a resource (a file or a db),
    defined by the latest scans and actions.
    """

    #: The resource is currently malicious or suspicious.
    FOUND = "found"
    #: *Db only*.
    #: The resource is added to the cleanup queue.
    CLEANUP_PENDING = "cleanup_pending"
    #: *Files*.
    #: Attempting to backup the file.
    #:
    #: *DBs*.
    #: MDS cleanup process was started.
    CLEANUP_STARTED = "cleanup_started"
    #: The resource is successfuly cleaned from malware.
    CLEANUP_DONE = "cleanup_done"
    #: *File only*.
    #: The file content (or the whole file, based on
    #: settings) is removed.
    CLEANUP_REMOVED = "cleanup_removed"
    #: *File only*.
    #: The resource requires MyImunify protection for the cleanup
    CLEANUP_REQUIRES_MYIMUNIFY_PROTECTION = (
        "cleanup_requires_myimunify_protection"
    )
    #: *Db only*.
    #: The resource is added to the restore queue.
    CLEANUP_RESTORE_PENDING = "cleanup_restore_pending"
    CLEANUP_REMOTE_RESTORE_PENDING = "cleanup_remote_restore_pending"
    #: *Db only*.
    #: MDS restore process has started.
    CLEANUP_RESTORE_STARTED = "cleanup_restore_started"
    #: *File only*.
    #: Similar to :attr:`CLEANUP_STARTED`, the async restoration process has
    #: started, but the file is not yet :attr:`RESTORED_FROM_BACKUP`.
    RESTORE_FROM_BACKUP_STARTED = "restore_from_backup_started"
    #: *File only*.
    #: A clean version of the file was found in backups and successfully
    #: restored.
    RESTORED_FROM_BACKUP = "restored_from_backup"

    # TODO: should we add
    # CLEANUP_RESTORE_PENDING, CLEANUP_RESTORE_STARTED here?
    CLEANUP = (CLEANUP_PENDING, CLEANUP_STARTED, CLEANUP_DONE, CLEANUP_REMOVED)
    RESTORABLE = (CLEANUP_DONE, CLEANUP_REMOVED)
    CLEANED = (CLEANUP_DONE, CLEANUP_REMOVED)


class QueuedScanState(Enum):
    stopped = "stopped"
    running = "running"
    queued = "queued"


class MalwareScanResourceType(Enum):
    FILE = "file"
    DB = "db"


class ExitDetachedScanType:
    """How/why detached scan ended."""

    #: It was stopped by a user.
    STOPPED = "stopped"
    #: It was aborted for other reasons.
    ABORTED = "aborted"


NOTIFY, CLEANUP, CLEANUP_ON_SCHEDULE = (
    "notify",
    "cleanup",
    "cleanup_on_schedule",
)


class VulnerabilityHitStatus:
    #: The resource is currently vulnerable.
    VULNERABLE = "vulnerable"
    #: The resource is successfuly patched.
    PATCHED = "patched"
    #: The resource is successfuly reverted.
    REVERTED = "vulnerable_patch_reverted"
    #: The resource is in patching now.
    PATCH_IN_PROGRESS = "patch_in_progress"
