import json
import os
from pathlib import Path
from typing import Any

CONFIG_PATH = Path(os.getenv("APP_CONFIG", "/app/config.json"))

DEFAULT_CONFIG: dict[str, Any] = {
    "general": {
        "title": "AssetManager",
        "company_name": "",
        "logo": "",
        "custom_css": "",
    },
    "authentication": {
        "mode": "none",
        "session_secret_env": "SESSION_SECRET",
        "ldap": {
            "server": "",
            "port": 389,
            "use_ssl": False,
            "start_tls": False,
            "bind_dn": "",
            "bind_password_env": "LDAP_BIND_PASSWORD",
            "user_base_dn": "",
            "user_filter": "(sAMAccountName={username})",
            "display_name_attribute": "displayName",
            "email_attribute": "mail",
            "debug_logging": False
        }
    },
    "software": {
        "dispatch_delay_seconds": 2,
        "callback_base_url": "",
        "callback_timeout_minutes": 30,
        "callback_worker_count": 4,
        "callback_test_timeout_seconds": 10,
        "callback_test_verify_tls": True,
        "debug_logging": True,
        "debug_log_max_lines": 1000,
        "automatic_retry_enabled": True,
        "automatic_retry_max_age_hours": 12,
        "automatic_retry_interval_seconds": 60,
        "automatic_retry_max_retries": 3,
        "inventory_exclusion_rules": []
    },
    "privacy": {},
    "meshcentral": {
        "enabled": False,
        "url": "",
        "username": "",
        "password": "",
        "password_env": "MESHCENTRAL_PASSWORD",
        "tenant": "",
        "meshctrl_path": "/opt/meshcentral/node_modules/meshcentral/meshctrl.js",
        "verify_tls": True,
        "fallback_category_id": 1,
        "category_mapping": {"1": 1, "2": 1, "3": 1, "4": 1, "5": 1, "6": 1, "7": 1, "8": 1},
        "timeout_seconds": 120,
        "presence_enabled": True,
        "presence_interval_seconds": 30,
        "match_order": ["node_id", "mac_address", "serial_number"],
        "serial_match_manufacturer": False,
        "invalid_serial_values": ["unknown", "n/a", "none", "not specified", "to be filled by o.e.m."],
        "manufacturer_invalid_serial_rules": [{"manufacturer": "Synology", "values": ["123456789", "Unknown"]}],
        "create_missing_assets": True,
        "mark_missing_devices": True,
        "field_rules": {},
    },
}


def _merge(base: dict[str, Any], supplied: dict[str, Any]) -> dict[str, Any]:
    for section, values in supplied.items():
        if isinstance(values, dict) and isinstance(base.get(section), dict):
            base[section].update(values)
        else:
            base[section] = values
    return base


def load_config() -> dict[str, Any]:
    config = json.loads(json.dumps(DEFAULT_CONFIG))
    if CONFIG_PATH.exists():
        with CONFIG_PATH.open("r", encoding="utf-8") as handle:
            supplied = json.load(handle)
        _merge(config, supplied)
    mesh = config["meshcentral"]
    env_name = mesh.get("password_env")
    if env_name and os.getenv(env_name):
        mesh["password"] = os.getenv(env_name)
    return config


def save_config(config: dict[str, Any]) -> None:
    CONFIG_PATH.parent.mkdir(parents=True, exist_ok=True)
    current: dict[str, Any] = {}
    if CONFIG_PATH.exists():
        with CONFIG_PATH.open("r", encoding="utf-8") as handle:
            current = json.load(handle)
    _merge(current, config)
    temporary = CONFIG_PATH.with_suffix(CONFIG_PATH.suffix + ".tmp")
    with temporary.open("w", encoding="utf-8") as handle:
        json.dump(current, handle, ensure_ascii=False, indent=2)
        handle.write("\n")
    temporary.replace(CONFIG_PATH)


def public_config() -> dict[str, Any]:
    config = load_config()
    mesh = dict(config["meshcentral"])
    mesh["password"] = "***" if mesh.get("password") else ""
    return {
        "config_path": str(CONFIG_PATH),
        "general": dict(config.get("general", {})),
        "authentication": dict(config.get("authentication", {})),
        "software": dict(config.get("software", {})),
        "privacy": dict(config.get("privacy", {})),
        "meshcentral": mesh,
    }
