#!/usr/bin/env python3
"""
Helpers partagés pour les profils métier.
"""
from __future__ import annotations

from copy import deepcopy
from pathlib import Path
from typing import Any, Dict

try:
    import yaml
except Exception:
    yaml = None

from config_defaults import CONFIG_DIR, deep_merge_dict


DEFAULT_PROFILES_CONFIG_PATH = CONFIG_DIR / "profiles.default.yml"
RUNTIME_PROFILES_CONFIG_PATH = CONFIG_DIR / "profiles.yml"

_RUNTIME_PROFILES_OVERLAY_TEXT = """# Surcharge locale des profils métier.
# Source de vérité : config/profiles.default.yml
# Ne mettez ici que les écarts spécifiques à votre environnement.
#
# Exemples :
# default_profile: chuxx_strict
# profiles:
#   mon_profil:
#     label: Mon profil
#     description: Surcharge locale
#     require_manual_mask: true
#     force_disable_vlm: true
#     preferred_manual_mask_template: chcb/formulaire.yml
#     param_lists:
#       whitelist_phrases:
#       - Document validé DIM
#     dictionaries_overlay:
#       blacklist:
#         force_mask_terms:
#         - MON_ETAB
{}
"""

_FALLBACK_DEFAULT_PROFILES_TEXT = """version: 1
default_profile: standard_local
profiles:
  standard_local:
    label: Standard local
    description: Profil par défaut pour les traitements internes.
    require_manual_mask: false
    force_disable_vlm: false
    dictionaries_overlay: {}
  chuxx_strict:
    label: CHUXX strict
    description: Profil conservateur pour le CHUXX, orienté diffusion prudente.
    require_manual_mask: false
    force_disable_vlm: true
    dictionaries_overlay:
      blacklist:
        force_mask_terms:
        - CHUXX
        - Centre Hospitalier Universitaire XX
        - CENTRE HOSPITALIER UNIVERSITAIRE XX
  partage_recherche:
    label: Partage recherche
    description: Profil externe strict. Le masque manuel est recommandé pour les formulaires répétitifs.
    require_manual_mask: true
    force_disable_vlm: true
    dictionaries_overlay:
      blacklist:
        force_mask_terms:
        - CHUXX
        - Centre Hospitalier Universitaire XX
        - CENTRE HOSPITALIER UNIVERSITAIRE XX
  dossier_audit:
    label: Dossier audit
    description: Profil orienté traçabilité et reproductibilité.
    require_manual_mask: false
    force_disable_vlm: true
    dictionaries_overlay: {}
  demo:
    label: Démo
    description: Profil léger pour démonstration interne sur poste bureautique.
    require_manual_mask: false
    force_disable_vlm: true
    dictionaries_overlay: {}
"""

_FALLBACK_DEFAULT_PROFILES_DICT: Dict[str, Any] = {
    "version": 1,
    "default_profile": "standard_local",
    "profiles": {
        "standard_local": {
            "label": "Standard local",
            "description": "Profil par défaut pour les traitements internes.",
            "require_manual_mask": False,
            "force_disable_vlm": False,
            "dictionaries_overlay": {},
        },
        "chuxx_strict": {
            "label": "CHUXX strict",
            "description": "Profil conservateur pour le CHUXX, orienté diffusion prudente.",
            "require_manual_mask": False,
            "force_disable_vlm": True,
            "dictionaries_overlay": {
                "blacklist": {
                    "force_mask_terms": [
                        "CHUXX",
                        "Centre Hospitalier Universitaire XX",
                        "CENTRE HOSPITALIER UNIVERSITAIRE XX",
                    ],
                },
            },
        },
        "partage_recherche": {
            "label": "Partage recherche",
            "description": (
                "Profil externe strict. Le masque manuel est recommandé "
                "pour les formulaires répétitifs."
            ),
            "require_manual_mask": True,
            "force_disable_vlm": True,
            "dictionaries_overlay": {
                "blacklist": {
                    "force_mask_terms": [
                        "CHUXX",
                        "Centre Hospitalier Universitaire XX",
                        "CENTRE HOSPITALIER UNIVERSITAIRE XX",
                    ],
                },
            },
        },
        "dossier_audit": {
            "label": "Dossier audit",
            "description": "Profil orienté traçabilité et reproductibilité.",
            "require_manual_mask": False,
            "force_disable_vlm": True,
            "dictionaries_overlay": {},
        },
        "demo": {
            "label": "Démo",
            "description": "Profil léger pour démonstration interne sur poste bureautique.",
            "require_manual_mask": False,
            "force_disable_vlm": True,
            "dictionaries_overlay": {},
        },
    },
}


def read_default_profiles_text() -> str:
    try:
        return DEFAULT_PROFILES_CONFIG_PATH.read_text(encoding="utf-8")
    except Exception:
        return _FALLBACK_DEFAULT_PROFILES_TEXT


def read_runtime_profiles_overlay_text() -> str:
    return _RUNTIME_PROFILES_OVERLAY_TEXT


def load_default_profiles_dict() -> Dict[str, Any]:
    text = read_default_profiles_text()
    if yaml is not None:
        try:
            loaded = yaml.safe_load(text) or {}
            if isinstance(loaded, dict):
                return loaded
        except Exception:
            pass
    return deepcopy(_FALLBACK_DEFAULT_PROFILES_DICT)


def list_default_profile_keys() -> set[str]:
    data = load_default_profiles_dict()
    profiles = data.get("profiles", {}) or {}
    if not isinstance(profiles, dict):
        return set()
    return {str(key) for key in profiles}


def load_runtime_profiles_overlay_dict(path: Path | None = None) -> Dict[str, Any]:
    target = Path(path) if path is not None else RUNTIME_PROFILES_CONFIG_PATH
    if not target.exists() or yaml is None:
        return {}
    try:
        loaded = yaml.safe_load(target.read_text(encoding="utf-8")) or {}
        if isinstance(loaded, dict):
            return loaded
    except Exception:
        pass
    return {}


def load_effective_profiles_dict(path: Path | None = None) -> Dict[str, Any]:
    return deep_merge_dict(
        load_default_profiles_dict(),
        load_runtime_profiles_overlay_dict(path),
    )


def _normalize_string_list(values: Any) -> list[str]:
    if not isinstance(values, list):
        return []
    normalized: list[str] = []
    for value in values:
        text = str(value).strip()
        if text:
            normalized.append(text)
    return normalized


def _normalize_param_lists(value: Any) -> Dict[str, list[str]]:
    if not isinstance(value, dict):
        return {}
    return {
        "whitelist_phrases": _normalize_string_list(value.get("whitelist_phrases", [])),
        "blacklist_force_mask_terms": _normalize_string_list(
            value.get("blacklist_force_mask_terms", [])
        ),
        "additional_stopwords": _normalize_string_list(value.get("additional_stopwords", [])),
    }


def _write_runtime_profiles_overlay_dict(path: Path, data: Dict[str, Any]) -> Path:
    if yaml is None:
        raise RuntimeError("PyYAML indisponible")
    body = yaml.safe_dump(
        data or {},
        allow_unicode=True,
        default_flow_style=False,
        sort_keys=False,
    )
    header = (
        "# Surcharge locale des profils métier.\n"
        "# Source de vérité : config/profiles.default.yml\n"
        "# Les profils créés depuis la GUI sont enregistrés ici.\n"
    )
    path.write_text(header + "\n" + body, encoding="utf-8")
    return path


def ensure_runtime_profiles_config(path: Path | None = None) -> Path:
    target = Path(path) if path is not None else RUNTIME_PROFILES_CONFIG_PATH
    if not target.exists():
        target.parent.mkdir(parents=True, exist_ok=True)
        target.write_text(read_runtime_profiles_overlay_text(), encoding="utf-8")
    return target


def list_effective_profiles(path: Path | None = None) -> Dict[str, Dict[str, Any]]:
    data = load_effective_profiles_dict(path)
    profiles = data.get("profiles", {}) or {}
    if not isinstance(profiles, dict):
        return {}
    normalized: Dict[str, Dict[str, Any]] = {}
    for key, value in profiles.items():
        if not isinstance(value, dict):
            continue
        raw_param_lists = value.get("param_lists")
        has_param_lists = isinstance(raw_param_lists, dict)
        preferred_manual_mask_template = str(value.get("preferred_manual_mask_template") or "").strip()
        normalized[str(key)] = {
            "label": str(value.get("label") or key),
            "description": str(value.get("description") or ""),
            "require_manual_mask": bool(value.get("require_manual_mask", False)),
            "force_disable_vlm": bool(value.get("force_disable_vlm", False)),
            "dictionaries_overlay": deepcopy(value.get("dictionaries_overlay") or {}),
            "param_lists": _normalize_param_lists(raw_param_lists),
            "has_param_lists": has_param_lists,
            "preferred_manual_mask_template": preferred_manual_mask_template,
            "has_preferred_manual_mask_template": "preferred_manual_mask_template" in value,
        }
    return normalized


def get_default_profile_key(path: Path | None = None) -> str:
    data = load_effective_profiles_dict(path)
    key = str(data.get("default_profile") or "").strip()
    profiles = list_effective_profiles(path)
    if key and key in profiles:
        return key
    if profiles:
        return next(iter(profiles))
    return "standard_local"


def save_runtime_profile(
    profile_key: str,
    profile_spec: Dict[str, Any],
    path: Path | None = None,
    *,
    set_default: bool = False,
) -> Path:
    target = ensure_runtime_profiles_config(path)
    data = load_runtime_profiles_overlay_dict(target)
    if not isinstance(data, dict):
        data = {}

    profiles = data.get("profiles")
    if not isinstance(profiles, dict):
        profiles = {}
        data["profiles"] = profiles

    normalized_spec: Dict[str, Any] = {
        "label": str(profile_spec.get("label") or profile_key),
        "description": str(profile_spec.get("description") or ""),
        "require_manual_mask": bool(profile_spec.get("require_manual_mask", False)),
        "force_disable_vlm": bool(profile_spec.get("force_disable_vlm", False)),
        "dictionaries_overlay": deepcopy(profile_spec.get("dictionaries_overlay") or {}),
    }

    if profile_spec.get("has_param_lists") or "param_lists" in profile_spec:
        normalized_spec["param_lists"] = _normalize_param_lists(profile_spec.get("param_lists"))

    if (
        profile_spec.get("has_preferred_manual_mask_template")
        or "preferred_manual_mask_template" in profile_spec
    ):
        normalized_spec["preferred_manual_mask_template"] = str(
            profile_spec.get("preferred_manual_mask_template") or ""
        ).strip()

    profiles[str(profile_key)] = normalized_spec
    if set_default:
        data["default_profile"] = str(profile_key)

    return _write_runtime_profiles_overlay_dict(target, data)


def set_runtime_default_profile(profile_key: str, path: Path | None = None) -> Path:
    target = ensure_runtime_profiles_config(path)
    data = load_runtime_profiles_overlay_dict(target)
    if not isinstance(data, dict):
        data = {}
    data["default_profile"] = str(profile_key)
    return _write_runtime_profiles_overlay_dict(target, data)


def delete_runtime_profile(profile_key: str, path: Path | None = None) -> Path:
    target = ensure_runtime_profiles_config(path)
    data = load_runtime_profiles_overlay_dict(target)
    if not isinstance(data, dict):
        data = {}

    profiles = data.get("profiles")
    if isinstance(profiles, dict):
        profiles.pop(str(profile_key), None)
        if not profiles:
            data.pop("profiles", None)

    if str(data.get("default_profile") or "").strip() == str(profile_key):
        data["default_profile"] = "standard_local"

    return _write_runtime_profiles_overlay_dict(target, data)
