"""Configuration des zones d'extraction éditable via l'overlay UI.

Les coordonnées sont relatives (0..1) dans l'image source. Elles sont chargées
au démarrage du pipeline et utilisées à la place des constantes en dur dans
`pipeline/prompts.py` et `pipeline/checkboxes.py` — avec fallback sur ces
constantes si la config n'est pas présente, pour ne pas casser l'existant.

Structure :
{
  "recueil": {
    "codage_reco":       {"x1":0.77, "y1":0.330, "x2":0.97, "y2":0.490, "description":"..."},
    "accord_checkbox":   {"x1":..., "y1":..., "x2":..., "y2":..., "description":"..."},
    "desaccord_checkbox":{...}
  },
  "concertation_2": {...}
}

Un fichier unique `zones_config.json` à la racine du projet, ou au chemin pointé
par la variable d'env `OGC_ZONES_CONFIG`.
"""
from __future__ import annotations

import json
import os
from pathlib import Path

DEFAULT_CONFIG_PATH = Path(
    os.environ.get("OGC_ZONES_CONFIG", "zones_config.json")
)


# Zones par défaut, identiques aux constantes actuelles dans prompts.py et
# checkboxes.py. Sert de fallback et de "mise à jour initiale" quand le
# fichier n'existe pas encore.
DEFAULTS: dict = {
    "recueil": {
        "codage_reco": {
            "x1": 0.77, "y1": 0.330, "x2": 0.97, "y2": 0.490,
            "description": "Colonne Recodage (DP / DR / DAS) — exclut le bloc Actes",
        },
        "accord_checkbox": {
            "x1": 0.588, "y1": 0.838, "x2": 0.622, "y2": 0.860,
            "description": "Case à cocher 'Accord'",
        },
        "desaccord_checkbox": {
            "x1": 0.588, "y1": 0.858, "x2": 0.622, "y2": 0.880,
            "description": "Case à cocher 'Désaccord'",
        },
    },
}


def load_config(path: Path = DEFAULT_CONFIG_PATH) -> dict:
    """Charge la config JSON, ou retourne les defaults si absente."""
    if not path.exists():
        return _deep_copy(DEFAULTS)
    try:
        raw = json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return _deep_copy(DEFAULTS)
    # Merge : les defaults sont une base, la config utilisateur vient par-dessus
    merged = _deep_copy(DEFAULTS)
    for page, zones in raw.items():
        merged.setdefault(page, {}).update(zones)
    return merged


def save_config(cfg: dict, path: Path = DEFAULT_CONFIG_PATH) -> Path:
    path.write_text(json.dumps(cfg, ensure_ascii=False, indent=2), encoding="utf-8")
    return path


def get_zone(page_type: str, zone_name: str,
             config: dict | None = None) -> tuple[float, float, float, float] | None:
    """Récupère une zone depuis la config ou les defaults.

    Retourne (x1, y1, x2, y2) ou None si inconnue.
    """
    cfg = config or load_config()
    z = cfg.get(page_type, {}).get(zone_name)
    if not isinstance(z, dict):
        return None
    try:
        return (float(z["x1"]), float(z["y1"]), float(z["x2"]), float(z["y2"]))
    except (KeyError, ValueError, TypeError):
        return None


def _deep_copy(d: dict) -> dict:
    return json.loads(json.dumps(d))
