// A small social graph in Neo4j over its HTTP API: nodes and relationships made with parameters,
// rows read back, statements together in a transaction, and a transaction held across calls.
//
//   NEO4J_API_URL=http://localhost:7474 NEO4J_USER=neo4j NEO4J_PASSWORD=... quillo run neo4j.qwe
//   then open /setup, /friends?name=Ada, /together, /held, /count and /broken

use "neo4j.qwer"

route GET /setup {
    let ada = neo4j_query("CREATE (n:Person {name: $name, age: $age}) RETURN n", { name: "Ada", age: 36 })?
    neo4j_query("CREATE (n:Person {name: $name, age: $age}) RETURN n", { name: "Grace", age: 45 })?
    neo4j_query("CREATE (n:Person {name: $name, age: $age}) RETURN n", { name: "Alan", age: 41 })?
    let knows = neo4j_query("MATCH (a:Person {name: $a}), (b:Person {name: $b}) CREATE (a)-[r:KNOWS]->(b) RETURN type(r) AS kind", { a: "Ada", b: "Grace" })?
    neo4j_query("MATCH (a:Person {name: $a}), (b:Person {name: $b}) CREATE (a)-[r:KNOWS]->(b) RETURN type(r) AS kind", { a: "Ada", b: "Alan" })?
    { node: ada.rows[0].n, created: ada.stats.nodes_created, kind: knows.rows[0].kind, relationships: knows.stats.relationships_created }
}

route GET /friends ? { name: str } = {
    friends: neo4j_query("MATCH (a:Person {name: $name})-[:KNOWS]->(b) RETURN b.name AS friend, b.age AS age ORDER BY friend", { name: name })?.rows,
    person: neo4j_one("MATCH (n:Person {name: $name}) RETURN n.age AS age", { name: name })?,
    nobody: neo4j_one("MATCH (n:Person {name: $name}) RETURN n.age AS age", { name: "Nobody" })?,
}

// Two statements at once, then two where the second is wrong: neither of those two stays.
route GET /together {
    let good = neo4j_transaction([
        { cypher: "CREATE (n:Person {name: $name, age: $age}) RETURN n", params: { name: "Edsger", age: 72 } },
        { cypher: "CREATE (n:Person {name: $name, age: $age}) RETURN n", params: { name: "Barbara", age: 81 } },
    ])?
    let bad = neo4j_transaction([
        { cypher: "CREATE (n:Person {name: $name, age: $age}) RETURN n", params: { name: "Ghost", age: 1 } },
        { cypher: "THIS IS NOT CYPHER" },
    ])
    { made: len(good), error: bad.error, ghost: neo4j_one("MATCH (n:Person {name: $name}) RETURN n.age AS age", { name: "Ghost" })? }
}

route GET /held {
    let t = neo4j_begin()?
    neo4j_run(t, "CREATE (n:Person {name: $name, age: $age}) RETURN n", { name: "Tony", age: 90 })?
    let inside = neo4j_run(t, "MATCH (n:Person {name: $name}) RETURN n.age AS age", { name: "Tony" })?.rows
    let outside = neo4j_one("MATCH (n:Person {name: $name}) RETURN n.age AS age", { name: "Tony" })?
    neo4j_commit(t)?
    let after = neo4j_one("MATCH (n:Person {name: $name}) RETURN n.age AS age", { name: "Tony" })?
    let u = neo4j_begin()?
    neo4j_run(u, "CREATE (n:Person {name: $name, age: $age}) RETURN n", { name: "Undone", age: 1 })?
    neo4j_rollback(u)?
    { inside: inside, outside: outside, after: after, undone: neo4j_one("MATCH (n:Person {name: $name}) RETURN n.age AS age", { name: "Undone" })? }
}

// The stand-in reports one deadlock here; the retry is not visible to the program.
route GET /count = neo4j_one("MATCH (n:Person) RETURN count(n) AS people", null)?

route GET /broken = {
    syntax: neo4j_query("MATCH (n RETURN n", null).error,
    missing: neo4j_query("MATCH (n:Person {name: $name}) RETURN n.age AS age", null).error,
    label: neo4j_quote_name("odd`label")?,
}
