import re

import schema
from clarabot_nano_common.config.utility import writable_directory, valid_absolute_path

from clarabot_nano.common.config.utility import valid_custom_service_port


class LocalConfigSchema(schema.Schema):
    def validate(self, data, _is_local_config_schema=True):
        data = super(LocalConfigSchema, self).validate(data, _is_local_config_schema=False)
        if _is_local_config_schema:
            for java_name in ["tika", "solr", "picoture"]:
                xms_name = f"{java_name}_heap_init"
                xmx_name = f"{java_name}_heap_size"
                if self._parse_java_heap_arg(data[xms_name]) > self._parse_java_heap_arg(data[xmx_name]):
                    raise schema.SchemaError(f"Heap arguments are invalid for {java_name}", None)
            if data["cool_wopi_port"] is not None and data["cool_wopi_port"] == data["cool_server_port"]:
                raise schema.SchemaError(f"The cool_*_port configs must not be equal", None)
        return data

    def _parse_java_heap_arg(self, xm: str) -> int:
        xm = xm.lower()
        if xm.endswith("k"):
            return int(xm[:-1]) * 1024
        if xm.endswith("m"):
            return int(xm[:-1]) * 1024 * 1024
        if xm.endswith("g"):
            return int(xm[:-1]) * 1024 * 1024 * 1024
        return int(xm)


LOCAL_CONFIG_SCHEMA = LocalConfigSchema(
    {
        "source": str,  # denormalized absolute path of the file the local config was loaded from
        "log_config": dict,  # no deep checking
        "config_dir": schema.And(str, writable_directory),
        "data_dir": schema.And(str, writable_directory),
        "pid_dir": schema.And(str, len),
        "solr_dir": str,
        "solr_data_dir": schema.And(str, len),
        "solr_logs_dir": schema.And(str, len),
        "solr_heap_init": schema.Regex(r"^\d+[KMG]?$", re.IGNORECASE),
        "solr_heap_size": schema.Regex(r"^\d+[KMG]?$", re.IGNORECASE),
        "tika_dir": str,
        "tika_logs_dir": schema.And(str, len),
        "remote_admin_policy": schema.And(str, lambda s: s in ("allow", "restrict", "deny")),
        "localhost_network_security": schema.And(str, lambda s: s in ("ec", "basic")),
        "control_api_path": schema.Or(str, None),
        "control_api_mode": schema.Or(schema.And(int, lambda i: 0o700 <= i <= 0o777), None),
        "control_api_group": schema.Or(str, int, None),
        "path_unicode_normalization": schema.And(str, lambda s: s in ("default", "keep", "NFC", "NFD", "NFKC", "NFKD")),
        "restrict_sensitive_path_access": bool,
        "drive_restriction_paths": schema.And([str], lambda sl: all(valid_absolute_path(s) for s in sl)),
        "hook_reset": schema.Or(str, [str]),
        "hook_drive_info": schema.Or(str, [str]),
        "hook_vendor_data": schema.Or(str, [str]),
        "java_dir": schema.Or(str, None),
        "solr_bundle_dir": schema.Or(str, None),
        "tika_bundle_dir": schema.Or(str, None),
        "tika_heap_init": schema.Regex(r"^\d+[KMG]?$", re.IGNORECASE),
        "tika_heap_size": schema.Regex(r"^\d+[KMG]?$", re.IGNORECASE),
        "picoture_heap_init": schema.Regex(r"^\d+[KMG]?$", re.IGNORECASE),
        "picoture_heap_size": schema.Regex(r"^\d+[KMG]?$", re.IGNORECASE),
        "cool_wopi_port": schema.Or(schema.And(int, valid_custom_service_port), None),
        "cool_server_port": schema.And(int, valid_custom_service_port),
        schema.Optional(object): object,  # allow upgrades to introduce new configuration keys
    }
)
REMOTE_CONFIG_SCHEMA = schema.Schema(
    {
        "name": schema.And(str, lambda s: len(s) < 80),
        "admin_password": schema.Or(str, None),
        "drives": [
            schema.Schema(
                {
                    "path": str,
                    schema.Optional("rooms"): [str],
                    schema.Optional("backups"): [str],  # rooms (not served by this Nano) to make backups from
                    schema.Optional(object): object,  # allow upgrades to introduce new configuration keys
                }
            )
        ],
        "room_blocks": schema.Schema({schema.Optional(str): [str]}),
        "deny_anonymous": bool,
        "require_explicit_peer_trust": bool,
        "backup_interval": schema.And(int, lambda h: 48 * 60 * 60 >= h >= 60 * 60),
        "indexed_languages": schema.Or(schema.And(str, lambda s: s == "all"), [str]),
        "indexer_sync_interval": schema.And(float, lambda f: f > 0),
        "indexer_remove_lost_count": schema.And(int, lambda i: i > 0),
        "indexer_force_sync_on_startup": bool,
        "indexer_minimum_delay_between_path_sync": schema.And(
            schema.Use(lambda f: min(float(f), 5.0)), lambda f: f >= 0
        ),
        "indexer_reserved_space": schema.And(int, lambda i: i > 0),
        "indexer_ignored_mimes": [str],
        "indexer_extract_size_limit": schema.And(int, lambda i: i >= 1000000),
        "indexer_extract_text_limit": schema.And(int, lambda i: i >= 10000),
        "remote_files_restriction": bool,
        "server_process_number": schema.And(int, lambda i: i > 0),
        "secrets": [bytes],
        schema.Optional(object): object,  # allow upgrades to introduce new configuration keys
    }
)
PARSED_CONFIG_SCHEMA = schema.Schema(
    {
        "local": LOCAL_CONFIG_SCHEMA,
        "remote": REMOTE_CONFIG_SCHEMA,
    }
)
DEFAULT_CONFIG = {
    "local": {
        # deny requests and commands that would change the configuration from remote sources without admin password
        "remote_admin_policy": "restrict",
        # auth & encrypt traffic
        "localhost_network_security": "ec",
        # optional control api over a Unix domain socket or named-pipe on Windows
        "control_api_path": None,
        "control_api_mode": None,
        "control_api_group": None,
        # path access and handling
        "path_unicode_normalization": "default",
        "restrict_sensitive_path_access": True,
        "drive_restriction_paths": [],
        # external hooks
        "hook_reset": [],
        "hook_drive_info": [],
        "hook_vendor_data": [],
        # use bundled dependencies by default
        "java_dir": "",
        "solr_bundle_dir": "",
        "solr_heap_init": "128M",
        "solr_heap_size": "512M",
        "tika_bundle_dir": "",
        "tika_heap_init": "64M",
        "tika_heap_size": "512M",
        "picoture_heap_init": "64M",
        "picoture_heap_size": "512M",
        # optional dependencies
        "cool_wopi_port": None,
        "cool_server_port": 9980,
    },
    "remote": {
        "admin_password": None,
        "drives": [],
        "room_blocks": {},
        "deny_anonymous": False,
        "require_explicit_peer_trust": False,
        "backup_interval": 43200,
        "indexed_languages": [],
        "indexer_sync_interval": 4.0,
        "indexer_remove_lost_count": 3,
        "indexer_force_sync_on_startup": True,
        "indexer_minimum_delay_between_path_sync": 0.0,
        "indexer_reserved_space": 3000,
        "indexer_ignored_mimes": [],
        "indexer_extract_size_limit": 256000000,
        "indexer_extract_text_limit": 1500000,
        "remote_files_restriction": True,
        "server_process_number": 1,
        "secrets": [],
    },
}
