"""Registra un vault en la app de escritorio Obsidian.

Opera sobre el archivo de configuracion de la app (~/.config/obsidian/obsidian.json),
NO sobre el sistema de archivos del vault. Anade (o actualiza) la entrada del vault
en la clave "vaults" de ese JSON para que Obsidian lo conozca en su lista de vaults.

Funcion impura: lee y escribe el archivo de configuracion de la app y crea un
backup .bak antes de sobreescribir. Idempotente: si ya existe una entrada con ese
path no la duplica, solo actualiza su flag "open" si difiere.
"""

import json
import os
import secrets
import shutil
import time


def _default_config_path() -> str:
    """Ruta por defecto del obsidian.json de la app de escritorio Obsidian."""
    return os.path.expanduser("~/.config/obsidian/obsidian.json")


def _load_config(config_path: str) -> dict:
    """Carga obsidian.json. Si no existe devuelve la estructura vacia base."""
    if not os.path.exists(config_path):
        return {"vaults": {}}
    with open(config_path, "r", encoding="utf-8") as f:
        data = json.load(f)
    if not isinstance(data, dict):
        raise ValueError(f"obsidian config is not a JSON object: {config_path}")
    if "vaults" not in data or not isinstance(data.get("vaults"), dict):
        data["vaults"] = {}
    return data


def _save_config(config_path: str, data: dict) -> str:
    """Escribe obsidian.json haciendo backup .bak previo si ya existia.

    Crea los directorios padre que falten. Devuelve la ruta del backup creado
    (cadena vacia si no habia archivo previo que respaldar).
    """
    parent = os.path.dirname(config_path)
    if parent:
        os.makedirs(parent, exist_ok=True)

    backup_path = ""
    if os.path.exists(config_path):
        backup_path = config_path + ".bak"
        shutil.copy2(config_path, backup_path)

    with open(config_path, "w", encoding="utf-8") as f:
        json.dump(data, f, ensure_ascii=False, indent=2)

    return backup_path


def register_obsidian_vault(
    vault_path: str,
    open: bool = False,
    config_path: str = "",
) -> dict:
    """Registra un vault en la lista de vaults de la app Obsidian.

    Args:
        vault_path: ruta al directorio del vault. Se normaliza a ruta absoluta.
        open: valor del flag "open" de la entrada del vault (si Obsidian deberia
            abrirlo al arrancar). Por defecto False.
        config_path: ruta al obsidian.json de la app. Vacio -> default
            ~/.config/obsidian/obsidian.json.

    Returns:
        dict con:
          - id: id hex de 16 chars de la entrada (existente o recien creada).
          - path: ruta absoluta normalizada del vault.
          - registered: True si se creo una entrada nueva.
          - already: True si ya existia una entrada con ese path.
          - open: flag "open" final de la entrada.
          - config_path: ruta del obsidian.json usado.
          - backup_path: ruta del backup .bak escrito (vacio si no habia archivo previo).

    Raises:
        ValueError: si el obsidian.json existente no es un objeto JSON valido.
        OSError: si la lectura/escritura del archivo falla por I/O.
    """
    cfg_path = config_path or _default_config_path()
    abs_path = os.path.abspath(os.path.expanduser(vault_path))

    data = _load_config(cfg_path)
    vaults = data["vaults"]

    # Idempotencia: buscar entrada existente por path.
    existing_id = None
    for vid, entry in vaults.items():
        if isinstance(entry, dict) and entry.get("path") == abs_path:
            existing_id = vid
            break

    if existing_id is not None:
        entry = vaults[existing_id]
        changed = entry.get("open") != open
        if changed:
            entry["open"] = open
        backup_path = _save_config(cfg_path, data) if changed else ""
        return {
            "id": existing_id,
            "path": abs_path,
            "registered": False,
            "already": True,
            "open": entry.get("open", open),
            "config_path": cfg_path,
            "backup_path": backup_path,
        }

    # Entrada nueva.
    new_id = secrets.token_hex(8)
    while new_id in vaults:
        new_id = secrets.token_hex(8)
    vaults[new_id] = {
        "path": abs_path,
        "ts": int(time.time() * 1000),
        "open": open,
    }
    backup_path = _save_config(cfg_path, data)
    return {
        "id": new_id,
        "path": abs_path,
        "registered": True,
        "already": False,
        "open": open,
        "config_path": cfg_path,
        "backup_path": backup_path,
    }


if __name__ == "__main__":
    import tempfile

    tmp = tempfile.mkdtemp()
    cfg = os.path.join(tmp, "obsidian.json")
    vault = os.path.join(tmp, "MiVault")
    os.makedirs(vault, exist_ok=True)

    r1 = register_obsidian_vault(vault, open=True, config_path=cfg)
    assert r1["registered"] is True and r1["already"] is False, r1
    assert r1["open"] is True, r1
    assert len(r1["id"]) == 16, r1

    r2 = register_obsidian_vault(vault, open=True, config_path=cfg)
    assert r2["already"] is True and r2["registered"] is False, r2
    assert r2["id"] == r1["id"], (r1, r2)
    assert os.path.isfile(cfg + ".bak") is False or True  # backup solo si cambio

    print("register_obsidian_vault smoke OK")
