<?php
namespace nur\sery\wip\schema\_scalar;

use Exception;
use nulib\cl;
use nulib\ref\schema\ref_analyze;
use nulib\ref\schema\ref_schema;
use nulib\ValueException;
use nur\sery\wip\schema\Result;
use Throwable;

/**
 * Class ScalarResult: résultat de l'analyse ou de la normalisation d'une valeur
 */
class ScalarResult extends Result {
  function isScalar(?ScalarResult &$result=null): bool { $result = $this; return true; }

  function getKeys(): array {
    return [null];
  }

  function select($key): Result {
    if ($key !== null) throw ValueException::invalid_key($key);
    return $this;
  }

  /** @var array  */
  protected $result;
  
  function reset(): void {
    $this->result = array_merge(
      array_fill_keys(static::KEYS, null), [
      "resultAvailable" => false,
      "present" => false,
      "available" => false,
      "null" => false,
      "valid" => false,
      "normalized" => false,
    ]);
  }

  function __get(string $name) {
    return $this->result[$name];
  }

  function __set(string $name, $value): void {
    $this->result[$name] = $value;
  }

  protected static function replace_key(string &$message, ?string $key): void {
    if ($key) {
      $message = str_replace("{key}", $key, $message);
    } else {
      $message = str_replace("{key}: ", "", $message);
      $message = str_replace("cette valeur", "la valeur", $message);
    }
  }

  protected static function replace_orig(string &$message, $origValue): void {
    $message = str_replace("{orig}", strval($origValue), $message);
  }

  protected function getMessage(string $key, ScalarSchema $schema): string {
    $message = cl::get($schema->messages, $key);
    if ($message !== null) return $message;
    return cl::get(ref_schema::MESSAGES, $key);
  }

  function setMissing(ScalarSchema $schema): int {
    $this->resultAvailable = true;
    $this->present = false;
    $this->available = false;
    if (!$schema->required) {
      $this->null = false;
      $this->valid = true;
      $this->normalized = true;
      return ref_analyze::NORMALIZED;
    } else {
      $messageKey = $this->messageKey = "missing";
      $message = $this->getMessage($messageKey, $schema);
      self::replace_key($message, $schema->name);
      $this->message = $message;
      return ref_analyze::MISSING;
    }
  }

  function setUnavailable(ScalarSchema $schema): int {
    $this->resultAvailable = true;
    $this->present = true;
    $this->available = false;
    if (!$schema->required) {
      $this->null = false;
      $this->valid = true;
      $this->normalized = true;
      return ref_analyze::NORMALIZED;
    } else {
      $messageKey = $this->messageKey = "unavailable";
      $message = $this->getMessage($messageKey, $schema);
      self::replace_key($message, $schema->name);
      $this->message = $message;
      return ref_analyze::UNAVAILABLE;
    }
  }

  function setNull(ScalarSchema $schema): int {
    $this->resultAvailable = true;
    $this->present = true;
    $this->available = true;
    $this->null = true;
    if ($schema->nullable) {
      $this->valid = true;
      $this->normalized = true;
      return ref_analyze::NORMALIZED;
    } else {
      $messageKey = $this->messageKey = "null";
      $message = $this->getMessage($messageKey, $schema);
      self::replace_key($message, $schema->name);
      $this->message = $message;
      return ref_analyze::NULL;
    }
  }

  function setInvalid($value, ScalarSchema $schema, ?Throwable $t=null): int {
    $this->resultAvailable = true;
    $this->present = true;
    $this->available = true;
    $this->null = false;
    $this->valid = false;
    $this->origValue = $value;
    $messageKey = $this->messageKey = "invalid";
    $message = $this->getMessage($messageKey, $schema);
    self::replace_key($message, $schema->name);
    self::replace_orig($message, $schema->orig);
    if ($t !== null) {
      $tmessage = ValueException::get_message($t);
      if ($tmessage) $message .= ": $tmessage";
    }
    $this->message = $message;
    return ref_analyze::INVALID;
  }

  function setValid($normalizedValue=null): int {
    $this->resultAvailable = true;
    $this->present = true;
    $this->available = true;
    $this->null = false;
    $this->valid = true;
    $this->normalizedValue = $normalizedValue;
    return ref_analyze::VALID;
  }

  function setNormalized(): int {
    $this->resultAvailable = true;
    $this->present = true;
    $this->available = true;
    $this->null = false;
    $this->valid = true;
    $this->normalized = true;
    return ref_analyze::NORMALIZED;
  }

  function throw(bool $throw): void {
    if ($throw) throw new ValueException($this->message);
  }
}
