import os
from datetime import timedelta
from functools import partial
from typing import Any

from defence360agent.contracts.config import (
    Config,
    Core,
    FilesUpdate,
    FromConfig,
    int_from_envvar,
)
from im360.utils import RulesLock
from defence360agent.utils.validate import IP

RBL_WHITELIST_FILE = "rbl_whitelist"
IPSET_LISTS_PATH = "/var/%s/%s-ipsetlists.db" % (
    Core.PRODUCT,
    Core.PRODUCT,
)


class AutoWhiteList:
    TTL_VALUE = FromConfig(
        "AUTO_WHITELIST",
        "timeout",
    )

    @classmethod
    def ttl_value(cls):
        return cls.TTL_VALUE * 60

    UNBLOCK_WHITELIST_TTL = FromConfig(
        "AUTO_WHITELIST",
        "after_unblock_timeout",
    )

    @classmethod
    def unblock_whitelist_ttl(cls):
        return cls.UNBLOCK_WHITELIST_TTL * 60


class CaptchaDOS:
    """
    X = TIMEFRAME
    T = MAX_COUNT
    N = TIMEOUT
    were taken from this doc:
    https://docs.google.com/a/cloudlinux.com/document/d/1uYMwy89dbF7FxKSzUehhJYcDadWhk0l5YRKuvZpSeq0/edit?usp=sharing
    """  # noqa: E501

    ENABLED = FromConfig("CAPTCHA_DOS", "enabled")
    TIME_FRAME = FromConfig("CAPTCHA_DOS", "time_frame")
    MAX_COUNT = FromConfig("CAPTCHA_DOS", "max_count")

    TIMEOUT = FromConfig("CAPTCHA_DOS", "timeout")


class CSFIntegration:
    ENABLED = FromConfig(
        section="CSF_INTEGRATION",
        option="catch_lfd_events",
    )


class ProactiveDefence:
    PHP_IMMUNITY = FromConfig(
        section="PROACTIVE_DEFENCE",
        option="php_immunity",
    )


class DOS:
    ENABLED = FromConfig("DOS", "enabled")
    INTERVAL = FromConfig("DOS", "interval")
    PER_PORT = FromConfig("DOS", "port_limits")
    DEFAULT_LIMIT = FromConfig("DOS", "default_limit")


class EnhancedDOS:
    ENABLED = FromConfig("ENHANCED_DOS", "enabled")
    TIMEFRAME = FromConfig("ENHANCED_DOS", "timeframe")
    PER_PORT = FromConfig("ENHANCED_DOS", "port_limits")
    DEFAULT_LIMIT = FromConfig("ENHANCED_DOS", "default_limit")
    ACTION = FromConfig("ENHANCED_DOS", "action")

    @staticmethod
    def as_dict() -> dict[str, Any]:
        return {
            name: getattr(EnhancedDOS, name)
            for name in dir(EnhancedDOS)
            if not name.startswith("_") and name.upper() == name
        }


class IncidentLogging:
    MIN_LOG_LEVEL = FromConfig("INCIDENT_LOGGING", "min_log_level")
    # automatically delete data from db, if it's older that NUM_DAYS
    NUM_DAYS = FromConfig("INCIDENT_LOGGING", "num_days")
    # max number of incidents in db
    LIMIT = FromConfig("INCIDENT_LOGGING", "limit")
    FREQUENCY = timedelta(days=1).total_seconds()


class LocalIncidentReporting:
    #: report [to server] only those local (without ip) incidents
    #  with severity no less (>=) than the given minimum
    MIN_SEVERITY = int_from_envvar("IMUNIFY360_NOIP_MIN_REPORT_SEVERITY", 4)


class Modsec:
    # MINIMAL|FULL
    RULESET = FromConfig("MOD_SEC", "ruleset")
    CMS_ACCOUNT_COMPROMISE_PREVENTION = FromConfig(
        "MOD_SEC", "cms_account_compromise_prevention"
    )
    APP_SPECIFIC_RULESET = FromConfig("MOD_SEC", "app_specific_ruleset")


class ModsecSensor:
    PLUGIN_ID = "modsec"
    SEND_ADDITIONAL_DATA = FromConfig("SEND_ADDITIONAL_DATA", "enable")


class ModsecBlockByCustomRules:
    RULES = FromConfig("MOD_SEC_BLOCK_BY_CUSTOM_RULE")
    DEFAULT_MAX_REPETITION = 2
    DEFAULT_PERIOD = 120

    @classmethod
    def get_limit(cls, rule):
        return cls.RULES[rule].get("max_incidents", cls.DEFAULT_MAX_REPETITION)

    @classmethod
    def get_timeout(cls, rule):
        return cls.RULES[rule].get("check_period", cls.DEFAULT_PERIOD)


class ModsecBlockBySeverity:
    ENABLED = FromConfig(
        "MOD_SEC_BLOCK_BY_SEVERITY",
        "enable",
    )
    CHECK_PERIOD = FromConfig(
        "MOD_SEC_BLOCK_BY_SEVERITY",
        "check_period",
    )
    MAX_REPETITION = FromConfig(
        "MOD_SEC_BLOCK_BY_SEVERITY",
        "max_incidents",
    )
    SEVERITY_LIMIT = FromConfig(
        "MOD_SEC_BLOCK_BY_SEVERITY",
        "severity_limit",
    )
    DENIED_NUM_LIMIT = FromConfig(
        "MOD_SEC_BLOCK_BY_SEVERITY",
        "denied_num_limit",
    )


class ModSecurityDirectives:
    """Values for `{check,fix} modsec directives` commands."""

    # https://github.com/SpiderLabs/ModSecurity/wiki/Reference-Manual-%28v2.x%29#Configuration_Directives
    SecAuditEngine = "RelevantOnly"
    SecConnEngine = "Off"
    SecRuleEngine = "On"


class NetworkInterface:
    """
    Applies or ignores Imunify360's firewall rules
    to specific network interfaces
    """

    ETH_DEVICE = FromConfig("NETWORK_INTERFACE", "eth_device")
    ETH6_DEVICE = FromConfig("NETWORK_INTERFACE", "eth6_device")
    ETH_DEVICE_SKIP = FromConfig("NETWORK_INTERFACE", "eth_device_skip")
    DEVICE_SKIP = "device_skip"

    @classmethod
    def get_interface_conf(cls):
        return {
            IP.V4: cls.ETH_DEVICE,
            IP.V6: cls.ETH6_DEVICE,
            cls.DEVICE_SKIP: cls.ETH_DEVICE_SKIP,
        }


class OssecSensor:
    PLUGIN_ID = "ossec"


class ControlPanelProtector:
    PLUGIN_ID = "control_panel_protector"


class CpHulkSensor:
    PLUGIN_ID = "CPHULK"


class Protector:
    RULE_EDIT_LOCK = RulesLock()


class Subsys:
    THIRD_PARTY_IDS = ("cPHulk", "fail2ban")
    # time 3rdpary IDS last check result will be cached for (30 seconds)
    THIRD_PARTY_IDS_CHECK_TIMEOUT = 30


class Webshield:
    ENABLE = FromConfig("WEBSHIELD", "enable")
    KNOWN_PROXIES_SUPPORT = FromConfig("WEBSHIELD", "known_proxies_support")
    SPLASH_SCREEN = FromConfig("WEBSHIELD", "splash_screen")
    SPLASH_CAPTCHA_SHOWN_LOG_ENTRY_RULE = 6068
    PANEL_PROTECTION = FromConfig("WEBSHIELD", "panel_protection")


class Scanlogd:
    ENABLE = FromConfig("SCANLOGD", "enable")


class WebServices:
    HTTP_PORTS = FromConfig(
        "WEB_SERVICES",
        "http_ports",
    )
    HTTPS_PORTS = FromConfig("WEB_SERVICES", "https_ports")


class Firewall:
    port_blocking_mode = FromConfig("FIREWALL", "port_blocking_mode")
    TCP_IN_IPV4 = FromConfig("FIREWALL", "TCP_IN_IPv4")
    TCP_OUT_IPV4 = FromConfig("FIREWALL", "TCP_OUT_IPv4")
    UDP_IN_IPV4 = FromConfig("FIREWALL", "UDP_IN_IPv4")
    UDP_OUT_IPV4 = FromConfig("FIREWALL", "UDP_OUT_IPv4")
    TCP_IN_IPV6 = FromConfig("FIREWALL", "TCP_IN_IPv6")
    TCP_OUT_IPV6 = FromConfig("FIREWALL", "TCP_OUT_IPv6")
    UDP_IN_IPV6 = FromConfig("FIREWALL", "UDP_IN_IPv6")
    UDP_OUT_IPV6 = FromConfig("FIREWALL", "UDP_OUT_IPv6")
    LOGGING_DISABLE_FLAG = "/var/imunify360/disable_iptables_logging"


class SMTPBlocking:
    getopt = partial(FromConfig, "SMTP_BLOCKING")

    ENABLED = getopt("enable")
    PORTS = getopt("ports")
    ALLOW_GROUPS = getopt("allow_groups")
    ALLOW_USERS = getopt("allow_users")
    ALLOW_LOCAL = getopt("allow_local")
    REDIRECT = getopt("redirect")


class StopManaging:
    """Categories to ignore by {validate,reset} agent's commands."""

    MODSEC_DIRECTIVES = FromConfig(
        section="STOP_MANAGING",
        option="modsec_directives",
    )


class ControlPanel:
    """
    Relates to actions to be performed by a host admin
    for compromised user accounts
    """

    COMPROMISED_USER_ADMIN_NOTIFICATION = FromConfig(
        "CONTROL_PANEL", "compromised_user_admin_notification"
    )
    COMPROMISED_USER_PASSWORD_RESET = FromConfig(
        "CONTROL_PANEL", "compromised_user_password_reset"
    )


class Permissions:
    ALLOW_LOCAL_RULES_MANAGEMENT = FromConfig(
        "PERMISSIONS", option="allow_local_rules_management"
    )
    ALLOW_LOCAL_IP_MANAGEMENT = FromConfig(
        "PERMISSIONS", option="allow_local_ip_management"
    )


CONFIG_SCHEMA_UNIFIED_ACCESS_LOGGER = {
    "groups": {
        "type": "dict",
        "schema": {
            "ipv4": {
                "type": "integer",
                "coerce": int,
                "default": 36004,
            },
            "ipv6": {
                "type": "integer",
                "coerce": int,
                "default": 36006,
            },
        },
        "default": {},
    },
    "rules": {
        "type": "dict",
        "keysrules": {
            "type": "string",
        },
        "valuesrules": {
            "type": "dict",
            "schema": {
                "id": {
                    "type": "integer",
                    "coerce": int,
                },
                "name": {"type": "string"},
                "severity": {
                    "type": "integer",
                    "coerce": int,
                    "min": 1,
                    "max": 15,
                },
            },
        },
    },
}


class UnifiedAccessLoggerConfig(Config):
    DISCLAIMER = """\
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
#
#   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#   DO NOT EDIT. INTERNAL USAGE ONLY.
#   !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
#
#   Direct modifications to this file prohibited.
#
# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #
    """

    def __init__(
        self,
        *,
        path=os.path.join(
            Core.CONFIG_DIR, Core.UNIFIED_ACCESS_LOGGER_CONFIGFILENAME
        ),
        validation_schema=CONFIG_SCHEMA_UNIFIED_ACCESS_LOGGER
    ):
        super().__init__(path=path, validation_schema=validation_schema)


class UnifiedAccessLogger:
    ENABLED = FromConfig("FIREWALL", "unified_access_logger")
    NFLOG_GROUPS = FromConfig("groups", config_cls=UnifiedAccessLoggerConfig)
    _RULES_NAMES = (
        WHITELIST,
        BLACKLIST,
        GRAYLIST,
        BLOCKED_BY_PORT,
        WHITELIST_COUNTRY,
        BLACKLIST_COUNTRY,
        SMTP,
    ) = (
        "im360-whitelist",
        "im360-blacklist",
        "im360-graylist",
        "im360-blocked-by-port",
        "im360-whitelisted-country",
        "im360-blacklisted-country",
        "im360-outgoing-blocked",
    )
    RULES = FromConfig("rules", config_cls=UnifiedAccessLoggerConfig)


FilesUpdate.DISABLED = FromConfig("FILES_UPDATE", "disabled_types")
FilesUpdate.DAYS_TO_KEEP = FromConfig("FILES_UPDATE", "days_to_keep")
