<?php
namespace nur\m;

use nur\m\base\QueryException;

/**
 * Interface IConn: une connexion à une base de données
 */
interface IConn {
  /**
   * obtenir des infos sur la connexion. le tableau retour fait au moins 3
   * éléments: [$dbname, $dbuser, $dbpass]
   */
  function getInfos(): array;

  /**
   * Démarrer une transaction si nécessaire. Si on est dans une transaction,
   * cette méthode est un NOP.
   *
   * @throws QueryException
   */
  function beginTransaction(): void;

  /**
   * Valider la transaction en cours, le cas échéant
   *
   * @throws QueryException
   */
  function commit(): void;

  /**
   * Annuler la transaction en cours, le cas échéant
   *
   * @throws QueryException
   */
  function rollback(): void;

  const EXECUTE_PARAMS_SCHEMA = [
    "transaction" => [null, false, "faut-il automatiquement démarrer une transaction pour cette requête?"],
    "stmt" => [null, true, "faut-il retourner un objet 'statement' pour récupérer le résultat de la requête?"],
    "num_rows" => [null, false, "faut-il retourner le nombre de ligne affectées?"],
    "last_insert_id" => [null, false, "faut-il retourner l'id de la dernière ligne ajoutée?"],
  ];

  const EXECUTE_PARAMS_DQL = [
    "transaction" => false,
    "stmt" => true,
    "num_rows" => false,
    "last_insert_id" => false,
  ];

  const EXECUTE_PARAMS_DML_UPDATE = [
    "transaction" => true,
    "stmt" => false,
    "num_rows" => true,
    "last_insert_id" => false,
  ];

  const EXECUTE_PARAMS_DML_INSERT = [
    "transaction" => true,
    "stmt" => false,
    "num_rows" => false,
    "last_insert_id" => true,
  ];

  const EXECUTE_RETURN_VALUE_SCHEMA = [
    "num_rows" => [null, null, "le nombre de ligne affectées par la requête"],
    "insert_id" => [null, null, "id de la dernière ligne ajoutée"],
    "stmt" => [null, null, "objet 'statement' permettant de récupérer le résultat de la requête"],
  ];

  /**
   * exécuter une requête générique de bas niveau (aucun traitement du type de
   * ceux effectués par {@link IRowIncarnation} n'est fait au préalable)
   *
   * $params est un tableau conforme à {@link EXECUTE_PARAMS_SCHEMA} et indique
   * les attendus concernant la requête et son résultat
   * - "transaction" indique qu'il faut démarrer une transaction si ce n'est
   * déjà le cas
   * - "stmt" indique que des lignes seront retournées, et qu'il ne faut pas
   * fermer l'objet "statement"
   * - "num_rows" indique qu'on souhaite avoir le nombre de lignes concernées
   * par l'opération de mise à jour
   * - "last_insert_id" indique qu'on souhaite avoir l'identifiant de la dernière
   * ligne ajoutée, si cela est possible
   *
   * Retourner un tableau conforme à {@link EXECUTE_RETURN_VALUE_SCHEMA}
   * - "stmt" est un objet 'statement' permettant de récupérer les lignes
   * résultat de la requête (la nature de cet objet dépend de l'implémentation)
   * - "num_rows" est le nom de lignes affectées par la requête
   * - "insert_id" est l'identifiant de la dernière ligne ajoutée
   *
   * @throws QueryException
   */
  function _execute(string $sql, ?array &$bindings=null, ?array $params=null): array;

  /**
   * frontend de bas niveau pour {@link _execute()} retournant toutes les lignes
   * résultat d'une requête de type DQL.
   *
   * @throws QueryException
   */
  function _fetchAll(string $sql, ?array &$bindings=null): array;

  /**
   * frontend de bas niveau pour {@link _execute()} permettant de récupérer
   * l'unique résultat (ou au moins le premier résultat) d'une requête de type
   * DQL. Retourner null si la requête ne retourne pas de résultat
   *
   * @throws QueryException
   */
  function _fetchFirst(string $sql, ?array &$bindings=null): ?array;

  /**
   * exécuter une requête qui met à jour jour des lignes. retourner le nombre de
   * lignes affectées.
   *
   * une transaction est commencée implicitement. les méthodes {@link commit()}
   * ou {@link rollback()} doivent être appelées explicitement.
   *
   * @throws QueryException
   */
  function _update(string $sql, ?array &$bindings=null): int;

  /**
   * retourner une instance par défaut de IQuery appropriée pour cette connexion.
   * l'instance est configurée par défaut pour une requête de type DQL
   */
  function query(?string $sql=null, ?array $filter=null, ?IRowIncarnation $incarnation=null): IQuery;

  #############################################################################

  /**
   * méthode de convenance retournant toutes les lignes résultat d'une requête
   * de type DQL.
   *
   * @throws QueryException
   */
  function all(string $sql, ?array $filter=null): array;

  /**
   * retourner une liste de valeurs [$value] tel que $value = $row[$name] pour
   * chaque ligne de résultat de {@link all()}
   *
   * NB: si $name===null, prendre le premier champ de $row
   *
   * @throws QueryException
   */
  function allVals(?string $name, string $sql, ?array $filter=null): array;

  /**
   * méthode de convenance permettant de récupérer l'unique résultat (ou au
   * moins le premier résultat) d'une requête de type DQL.
   *
   * Retourner null si la requête ne retourne pas de résultat
   *
   * @throws QueryException
   */
  function first(string $sql, ?array $filter=null): ?array;

  /**
   * retourner la valeur $row[$name] où $row est la valeur retournée par
   * {@link first()}. Retourner $default si {@link first()} ne retourne
   * pas de résultat
   *
   * NB: si $name===null, prendre le premier champ de $row
   *
   * @throws QueryException
   */
  function firstVal(?string $name, string $sql, ?array $filter=null, $default=null);

  /**
   * méthode de convenance pour exécuter une requête qui met à jour jour des
   * lignes. retourner le nombre de lignes affectées.
   *
   * une transaction est commencée si ce n'est pas déjà le cas. ensuite, la
   * transaction (celle déjà existante ou la nouvelle) est systématiquement
   * commitée.
   *
   * @throws QueryException
   */
  function update(string $sql, ?array $filter=null): int;
}