<?php
namespace nur\sery\db\_private;

use nur\sery\cl;
use nur\sery\str;
use nur\sery\ValueException;

trait Tselect {
  static function isa(string $sql): bool {
    return preg_match("/^select\b/i", $sql);
  }

  private static function add_prefix(string $col, ?string $prefix): string {
    if ($prefix === null) return $col;
    if (strpos($col, ".") !== false) return $col;
    return "$prefix$col";
  }

  /**
   * parser une chaine de la forme
   * "select [COLS] [from TABLE] [where CONDS] [order by ORDERS] [group by GROUPS] [having CONDS]"
   */
  static function parse(array $query, ?array &$bindings=null): string {
    # fusionner d'abord toutes les parties séquentielles
    $usersql = $tmpsql = self::merge_seq($query);

    ### vérifier la présence des parties nécessaires
    $sql = [];

    ## préfixe
    if (($prefix = $query["prefix"] ?? null) !== null) $sql[] = $prefix;

    ## select
    self::consume('(select(?:\s*distinct)?)\s*', $tmpsql, $ms);
    $sql[] = $ms[1];

    ## cols
    $usercols = [];
    if (self::consume('(.*?)\s*(?=$|\bfrom\b)', $tmpsql, $ms)) {
      if ($ms[1]) $usercols[] = $ms[1];
    }
    $colPrefix = $query["col_prefix"] ?? null;
    if ($colPrefix !== null) str::add_suffix($colPrefix, ".");
    $tmpcols = cl::withn($query["cols"] ?? null);
    $schema = $query["schema"] ?? null;
    if ($tmpcols !== null) {
      $cols = [];
      $index = 0;
      foreach ($tmpcols as $key => $col) {
        if ($key === $index) {
          $index++;
          $cols[] = $col;
          $usercols[] = self::add_prefix($col, $colPrefix);
        } else {
          $cols[] = $key;
          $usercols[] = self::add_prefix($col, $colPrefix)." as $key";
        }
      }
    } else {
      $cols = null;
      if ($schema && is_array($schema) && !in_array("*", $usercols)) {
        $cols = array_keys($schema);
        foreach ($cols as $col) {
          $usercols[] = self::add_prefix($col, $colPrefix);
        }
      }
    }
    if (!$usercols && !$cols) $usercols = [self::add_prefix("*", $colPrefix)];
    $sql[] = implode(", ", $usercols);

    ## from
    $from = $query["from"] ?? null;
    if (self::consume('from\s+([a-z_][a-z0-9_]*)\s*(?=;?\s*$|\bwhere\b)', $tmpsql, $ms)) {
      if ($from === null) $from = $ms[1];
      $sql[] = "from";
      $sql[] = $from;
    } elseif ($from !== null) {
      $sql[] = "from";
      $sql[] = $from;
    } else {
      throw new ValueException("expected table name: $usersql");
    }

    ## where
    $userwhere = [];
    if (self::consume('where\b\s*(.*?)(?=;?\s*$|\border\s+by\b)', $tmpsql, $ms)) {
      if ($ms[1]) $userwhere[] = $ms[1];
    }
    $where = cl::withn($query["where"] ?? null);
    if ($where !== null) self::parse_conds($where, $userwhere, $bindings);
    if ($userwhere) {
      $sql[] = "where";
      $sql[] = implode(" and ", $userwhere);
    }

    ## order by
    $userorderby = [];
    if (self::consume('order\s+by\b\s*(.*?)(?=;?\s*$|\bgroup\s+by\b)', $tmpsql, $ms)) {
      if ($ms[1]) $userorderby[] = $ms[1];
    }
    $orderby = cl::withn($query["order by"] ?? null);
    if ($orderby !== null) {
      $index = 0;
      foreach ($orderby as $key => $value) {
        if ($key === $index) {
          $userorderby[] = $value;
          $index++;
        } else {
          if ($value === null) $value = false;
          if (!is_bool($value)) {
            $userorderby[] = "$key $value";
          } elseif ($value) {
            $userorderby[] = $key;
          }
        }
      }
    }
    if ($userorderby) {
      $sql[] = "order by";
      $sql[] = implode(", ", $userorderby);
    }
    ## group by
    $usergroupby = [];
    if (self::consume('group\s+by\b\s*(.*?)(?=;?\s*$|\bhaving\b)', $tmpsql, $ms)) {
      if ($ms[1]) $usergroupby[] = $ms[1];
    }
    $groupby = cl::withn($query["group by"] ?? null);
    if ($groupby !== null) {
      $index = 0;
      foreach ($groupby as $key => $value) {
        if ($key === $index) {
          $usergroupby[] = $value;
          $index++;
        } else {
          if ($value === null) $value = false;
          if (!is_bool($value)) {
            $usergroupby[] = "$key $value";
          } elseif ($value) {
            $usergroupby[] = $key;
          }
        }
      }
    }
    if ($usergroupby) {
      $sql[] = "group by";
      $sql[] = implode(", ", $usergroupby);
    }

    ## having
    $userhaving = [];
    if (self::consume('having\b\s*(.*?)(?=;?\s*$)', $tmpsql, $ms)) {
      if ($ms[1]) $userhaving[] = $ms[1];
    }
    $having = cl::withn($query["having"] ?? null);
    if ($having !== null) self::parse_conds($having, $userhaving, $bindings);
    if ($userhaving) {
      $sql[] = "having";
      $sql[] = implode(" and ", $userhaving);
    }

    ## suffixe
    if (($suffix = $query["suffix"] ?? null) !== null) $sql[] = $suffix;

    ## fin de la requête
    self::check_eof($tmpsql, $usersql);
    return implode(" ", $sql);
  }
}
