<?php
namespace nur\sery\file\web;

use nur\sery\cl;
use nur\sery\file\FileReader;
use nur\sery\os\path;
use nur\sery\php\coll\BaseArray;
use nur\sery\ValueException;

/**
 * Class Upload: un fichier téléversé
 *
 * @property-read string $name
 * @property-read string $type
 * @property-read string $tmpName
 * @property-read int $error
 * @property-read int $size
 * @property-read string $fullPath
 */
class Upload extends BaseArray {
  const MESSAGES = [
    "invalid" => "Ceci n'est pas un fichier téléversé",
    "nofile" => "Aucun fichier n'a été fourni",
    "toobig" => "Le fichier que vous avez fourni est trop volumineux.",
    "unknown" => "Une erreur s'est produite pendant le transfert du fichier. Veuillez réessayer.",
  ];

  protected static function error(string $message) {
    return new ValueException(static::MESSAGES[$message]);
  }

  function __construct(?array $file, bool $required=true, bool $check=true) {
    parent::__construct($file);
    if ($check) $this->check($required);
  }

  function check(bool $required=true, bool $throw=true): bool {
    $file = $this->data;
    if ($file) {
      $name = $file["name"] ?? null;
      $type = $file["type"] ?? null;
      $error = $file["error"] ?? null;
      if (!is_scalar($name) || !is_scalar($type) || !is_scalar($error)) {
        if ($throw) throw static::error("invalid");
        else return false;
      }
      switch ($error) {
      case UPLOAD_ERR_OK:
        break;
      case UPLOAD_ERR_NO_FILE:
        if ($required) {
          if ($throw) throw self::error("nofile");
          else return false;
        }
        break;
      case UPLOAD_ERR_INI_SIZE:
      case UPLOAD_ERR_FORM_SIZE:
        if ($throw) throw self::error("toobig");
        else return false;
      default:
        if ($throw) self::error("unknown");
        else return false;
      }
    } elseif ($required) {
      if ($throw) throw static::error("nofile");
      else return false;
    }
    return true;
  }

  const _AUTO_PROPERTIES = [
    "tmpName" => "tmp_name",
    "fullPath" => "full_path",
  ];
  function &__get($name) {
    $name = static::_AUTO_PROPERTIES[$name] ?? $name;
    return parent::__get($name);
  }

  function isError(): bool {
    $error = $this->error;
    return $error !== UPLOAD_ERR_OK && $error !== UPLOAD_ERR_NO_FILE;
  }

  function isValid(): bool {
    return $this->error === UPLOAD_ERR_OK;
  }

  /**
   * retourner true si le nom du fichier a une des extensions de $exts
   *
   * @param string|array $exts une ou plusieurs extensions qui sont vérifiées
   */
  function isExt($exts): bool {
    if ($exts === null) return false;
    $ext = path::ext($this->name);
    $exts = cl::with($exts);
    return in_array($ext, $exts);
  }

  /** @var ?string chemin du fichier, s'il a été déplacé */
  protected $file;

  function moveTo(string $dest): bool {
    if ($this->file === null) {
      $moved = move_uploaded_file($this->tmpName, $dest);
      if ($moved) $this->file = $dest;
    } else {
      $moved = false;
    }
    return $moved;
  }

  function getFile(): FileReader {
    $file = $this->file ?? $this->tmpName;
    return new FileReader($file, "r+b");
  }
}
