#!/usr/bin/env catnip
# Requêtage JSON avec JMESPath
# JMESPath est un langage de requête pour JSON (utilisé par AWS CLI)

jmespath = import('jmespath')

# Struct pour encapsuler une requête nommée

struct Query {
    label; expr;
    run(self, data) => { jmespath.search(self.expr, data) }
}

show = (label, result) => { print(f"  {label} : {result}") }

# Données de test (réponse API d'exemple)

data = dict(
    users=list(dict(
            id=1,
            name="Alice",
            email="alice@example.com",
            role="admin",
            active=True,
            projects=list("alpha", "beta"),
        ), dict(
            id=2,
            name="Bob",
            email="bob@example.com",
            role="developer",
            active=True,
            projects=list("beta", "gamma"),
        ), dict(
            id=3,
            name="Charlie",
            email="charlie@example.com",
            role="developer",
            active=False,
            projects=list("alpha"),
        )),
    metadata=dict(
        total=3,
        page=1,
        per_page=10,
    )
)

# Accès simple

print("⇒ Accès simple")
simple_queries = list(
    Query("metadata.total", "metadata.total"),
    Query("metadata.page", "metadata.page"),
)
simple_queries.[(q) => { show(q.label, q.run(data)) }]

# Accès aux listes

print()
print("⇒ Accès aux listes")
list_queries = list(
    Query("Premier user", "users[0].name"),
    Query("Dernier user", "users[-1].name"),
)
list_queries.[(q) => { show(q.label, q.run(data)) }]

# Projection (extraire un champ de chaque élément)

print()
print("⇒ Projection")
proj_queries = list(
    Query("Tous les noms", "users[*].name"),
    Query("Tous les emails", "users[*].email"),
)
proj_queries.[(q) => { show(q.label, q.run(data)) }]

# Filtrage

print()
print("⇒ Filtrage")
filter_queries = list(
    Query("Admins", "users[?role=='admin'].name"),
    Query("Users actifs", "users[?active==`true`].name"),
)
filter_queries.[(q) => { show(q.label, q.run(data)) }]

devs = Query("Developers", "users[?role=='developer']").run(data)
show("Developers (count)", len(devs))

# Projection multiple (dict construit à la volée)

print()
print("⇒ Projection multiple (sélection de champs)")
subset = Query("Subset", "users[*].{nom: name, mail: email}").run(data)
print("  Subset:")
for u in subset {
    print(f"     {u}")
}
print("  Exemple premier élément:")
show("nom", subset[0]['nom'])
show("mail", subset[0]['mail'])

# Flatten (aplatir les listes)

print()
print("⇒ Flatten")
all_projects = Query("Projets", "users[*].projects[]").run(data)
show("Tous les projets (avec doublons)", all_projects)

# Fonctions JMESPath

print()
print("⇒ Fonctions JMESPath")
fn_queries = list(
    Query("Nombre d'users", "length(users)"),
    Query("Premier nom (sort)", "sort(users[*].name)[0]"),
    Query("Dernier nom (sort)", "sort(users[*].name)[-1]"),
)
fn_queries.[(q) => { show(q.label, q.run(data)) }]

# Conditions complexes

print()
print("⇒ Conditions complexes")
beta_users = Query("Users sur projet beta", "users[?contains(projects, 'beta')].name").run(data)
show("Users sur projet beta", beta_users)

# Dict imbriqué dans le résultat

print()
print("⇒ Dict imbriqué (retour structuré)")
summary = Query("Résumé", "{meta: metadata, admins: users[?role=='admin'].name, total_users: length(users)}").run(
    data
)
show("Résumé", summary)

# Exemple: réponse API paginée

api_response = dict(
    items=list(
        dict(sku="A001", price=29.99, stock=100),
        dict(sku="A002", price=49.99, stock=0),
        dict(sku="B001", price=19.99, stock=50),
        dict(sku="B002", price=99.99, stock=25),
    ),
    pagination=dict(
        next="/api/items?page=2",
        total_pages=5,
    )
)

print()
print("⇒ Exemple API e-commerce")
ecom_queries = list(
    Query("SKUs en stock", "items[?stock > `0`].sku"),
    Query("Produits > 50 EUR", "items[?price > `50`].{sku: sku, prix: price}")
)
ecom_queries.[(q) => { show(q.label, q.run(api_response)) }]

# Min/Max avec sort

print()
print("⇒ Min/Max")
cheapest = Query("Moins cher", "sort_by(items, &price)[0]").run(api_response)
most_expensive = Query("Plus cher", "sort_by(items, &price)[-1]").run(api_response)
show("Moins cher", f"{cheapest['sku']} - {cheapest['price']} EUR")
show("Plus cher", f"{most_expensive['sku']} - {most_expensive['price']} EUR")

# Compilation pour performance

print()
print("⇒ Expression compilée")
expr = jmespath.compile("users[?active].name")
result = expr.search(data)
show("Résultat", result)