"""Test LDAP/AD step-by-step (bind admin + search user + bind user).

Lit la config depuis app_secrets (PatchCenter). Optionnel : --user/--pass
pour tester un compte utilisateur après le bind admin.

Usage:
    python tools/test_ldap.py                          # test bind admin uniquement
    python tools/test_ldap.py --user moutaouakil-ext   # test bind admin + recherche user
    python tools/test_ldap.py --user xx --pass yy      # + bind user (verifie mdp)
"""
import os
import sys
import argparse
import base64
from sqlalchemy import create_engine, text

DATABASE_URL = os.getenv("DATABASE_URL_DEMO") or os.getenv("DATABASE_URL") \
    or "postgresql://patchcenter:PatchCenter2026!@localhost:5432/patchcenter_demo"

try:
    from ldap3 import Server, Connection, ALL
    from cryptography.fernet import Fernet
except ImportError:
    print("[ERR] pip install ldap3 cryptography")
    sys.exit(1)


def get_secret(conn, key):
    secret = os.getenv("SECRET_KEY",
                       "slpm-patchcenter-secret-key-2026-change-in-production")
    raw = secret.encode()[:32].ljust(32, b"\0")
    f = Fernet(base64.urlsafe_b64encode(raw))
    row = conn.execute(text("SELECT value FROM app_secrets WHERE key=:k"), {"k": key}).fetchone()
    if not row or not row.value:
        return None
    try:
        return f.decrypt(row.value.encode()).decode()
    except Exception:
        return row.value


def main():
    p = argparse.ArgumentParser()
    p.add_argument("--user", help="Username AD a tester (sAMAccountName)")
    p.add_argument("--pass", dest="pwd", help="Mot de passe user a tester")
    args = p.parse_args()

    engine = create_engine(DATABASE_URL)
    conn = engine.connect()
    cfg = {
        "server": get_secret(conn, "ldap_server"),
        "base_dn": get_secret(conn, "ldap_base_dn"),
        "bind_dn": get_secret(conn, "ldap_bind_dn"),
        "bind_pwd": get_secret(conn, "ldap_bind_pwd"),
        "user_filter": get_secret(conn, "ldap_user_filter") or "(sAMAccountName={username})",
        "email_attr": get_secret(conn, "ldap_email_attr") or "mail",
        "name_attr": get_secret(conn, "ldap_name_attr") or "displayName",
    }
    conn.close()

    print(f"[INFO] Server : {cfg['server']}")
    print(f"[INFO] Base DN : {cfg['base_dn']}")
    print(f"[INFO] Bind DN : {cfg['bind_dn']}")
    print(f"[INFO] Bind pwd: {'***configure***' if cfg['bind_pwd'] else 'VIDE'}")
    print(f"[INFO] User filter: {cfg['user_filter']}")
    print()

    if not cfg["server"] or not cfg["bind_dn"] or not cfg["bind_pwd"]:
        print("[ERR] Config incomplete dans app_secrets. Configure /settings onglet LDAP d'abord.")
        return

    use_ssl = cfg["server"].startswith("ldaps://")
    server = Server(cfg["server"], get_info=ALL, use_ssl=use_ssl)

    # Step 1: bind admin
    print("[STEP 1] Bind compte admin...")
    try:
        c = Connection(server, user=cfg["bind_dn"], password=cfg["bind_pwd"], auto_bind=True)
        print(f"  OK Bind admin reussi. Server info: {server.info.naming_contexts if server.info else 'N/A'}")
    except Exception as e:
        print(f"  KO Bind admin echec: {type(e).__name__}: {e}")
        return

    if not args.user:
        c.unbind()
        print("\n[OK] Bind admin OK. Lance avec --user <login> pour tester la recherche.")
        return

    # Step 2: search user
    print(f"\n[STEP 2] Recherche user {args.user}...")
    user_filter = cfg["user_filter"].replace("{username}", args.user)
    try:
        c.search(cfg["base_dn"], user_filter,
                 attributes=[cfg["email_attr"], cfg["name_attr"], "distinguishedName", "memberOf"])
    except Exception as e:
        c.unbind()
        print(f"  KO Search echec: {e}")
        return

    if not c.entries:
        c.unbind()
        print(f"  KO User '{args.user}' introuvable (filtre: {user_filter})")
        return

    entry = c.entries[0]
    user_dn = entry.entry_dn
    email = str(getattr(entry, cfg["email_attr"], ""))
    name = str(getattr(entry, cfg["name_attr"], ""))
    print(f"  OK Trouve:")
    print(f"     DN    : {user_dn}")
    print(f"     Email : {email}")
    print(f"     Name  : {name}")
    if hasattr(entry, "memberOf"):
        groups = list(entry.memberOf.values) if entry.memberOf else []
        print(f"     Groups: {len(groups)} appartenances")
        for g in groups[:5]:
            print(f"       - {g}")
    c.unbind()

    if not args.pwd:
        print("\n[OK] Recherche OK. Lance avec --pass pour tester l'auth.")
        return

    # Step 3: bind user
    print(f"\n[STEP 3] Bind user {args.user} avec mdp fourni...")
    try:
        uc = Connection(server, user=user_dn, password=args.pwd, auto_bind=True)
        uc.unbind()
        print("  OK Authentification reussie.")
    except Exception as e:
        print(f"  KO Auth echec: {e}")


if __name__ == "__main__":
    main()
