<?php
namespace nulib\cli;

use nulib\A;
use nulib\app\cli\Application;
use nulib\db\Capacitor;
use nulib\db\CapacitorChannel;
use nulib\db\CapacitorStorage;
use nulib\ext\yaml;
use nulib\file\Stream;
use nulib\output\msg;

abstract class AbstractStorageApp extends Application {
  const ACTION_RESET = 0, ACTION_QUERY = 1, ACTION_SQL = 2;

  protected ?string $tableName = null;

  protected ?string $channelClass = null;

  protected int $action = self::ACTION_QUERY;

  protected bool $recreate = true;

  protected ?array $args = null;

  protected static function isa_cond(string $arg, ?array &$ms=null): bool {
    return preg_match('/^(.+?)\s*(=|<>|<|>|<=|>=|(?:is\s+)?null|(?:is\s+)?not\s+null)\s*(.*)$/', $arg, $ms);
  }

  protected function storageCtl(CapacitorStorage $storage): void {
    $args = $this->args;

    $channelClass = $this->channelClass;
    $tableName = $this->tableName;
    if ($channelClass === null && $tableName === null) {
      $name = A::shift($args);
      if ($name !== null) {
        if (!$storage->channelExists($name, $row)) {
          self::die("$name: nom de canal de données introuvable");
        }
        if ($row["class_name"] !== "class@anonymous") $channelClass = $row["class_name"];
        else $tableName = $row["table_name"];
      }
    }
    if ($channelClass !== null) {
      $channelClass = str_replace("/", "\\", $channelClass);
      $channel = new $channelClass;
    } elseif ($tableName !== null) {
      $channel = new class($tableName) extends CapacitorChannel {
        function __construct(?string $name=null) {
          parent::__construct($name);
          $this->tableName = $name;
        }
      };
    } else {
      $found = false;
      foreach ($storage->getChannels() as $row) {
        msg::print($row["name"]);
        $found = true;
      }
      if ($found) self::exit();
      self::die("Vous devez spécifier le canal de données");
    }
    $capacitor = new Capacitor($storage, $channel);

    switch ($this->action) {
    case self::ACTION_RESET:
      $capacitor->reset($this->recreate);
      break;
    case self::ACTION_QUERY:
      if (!$args) {
        # lister les id
        $out = new Stream(STDOUT);
        $primaryKeys = $storage->getPrimaryKeys($channel);
        $rows = $storage->db()->all([
          "select",
          "cols" => $primaryKeys,
          "from" => $channel->getTableName(),
        ]);
        $out->fputcsv($primaryKeys);
        foreach ($rows as $row) {
          $rowIds = $storage->getRowIds($channel, $row);
          $out->fputcsv($rowIds);
        }
      } else {
        # afficher les lignes correspondantes
        if (count($args) == 1 && !self::isa_cond($args[0])) {
          $filter = $args[0];
        } else {
          $filter = [];
          $ms = null;
          foreach ($args as $arg) {
            if (self::isa_cond($arg, $ms)) {
              $filter[$ms[1]] = [$ms[2], $ms[3]];
            } else {
              $filter[$arg] = ["not null"];
            }
          }
        }
        $first = true;
        $capacitor->each($filter, function ($row) use (&$first) {
          if ($first) $first = false;
          else echo "---\n";
          yaml::dump($row);
        });
      }
      break;
    case self::ACTION_SQL:
      echo $capacitor->getCreateSql()."\n";
      break;
    }
  }
}
