<?php
namespace nur\v;

use nur\A;
use nur\str;
use nur\v\model\IPage;

/**
 * Class prefix: gestion des préfixes pour les URLs
 */
class prefix {
  static function get_base_url(): string {
    $base_url = getenv("BASE_URL");
    if ($base_url !== false) {
      return str::without_suffix("/", parse_url($base_url, PHP_URL_PATH));
    } else {
      return "";
    }
  }

  /**
   * obtenir le chemin par défaut du script courant.
   * - si la variable d'environnement BASE_URL est définie, alors la valeur est
   *   exacte.
   * - sinon, on part du principe que l'application ne sert des pages qu'à sa
   *   racine, ce qui n'est pas forcément vrai
   *
   * la valeur retournée est
   * - soit le chemin avec le préfixe '/' e.g '/index.php'
   * - soir la chaine vide ''
   */
  static function get_default_self(): string {
    $script_name = $_SERVER["SCRIPT_NAME"];
    $base_url = getenv("BASE_URL");
    if ($base_url !== false) {
      # d'abord, isoler le chemin de $base_url
      $base_url = str::without_suffix("/", parse_url($base_url, PHP_URL_PATH));
      # puis enlever le préfixe
      return str::without_prefix($base_url, $script_name);
    }
    $pos = strrpos($script_name, "/");
    if ($pos === false) return "";
    return substr($script_name, $pos);
  }

  static function compute(?string $self): string {
    if ($self !== null) {
      if (substr($self, 0, 1) != "/") $self = "/$self";
    } else {
      $self = self::get_default_self();
    }
    $php_self = $_SERVER["PHP_SELF"];
    $pos = strpos($php_self, $self);
    if ($pos === false) return "";
    $prefix = "";
    $offset = $pos + 1;
    while (($pos = strpos($php_self, "/", $offset)) !== false) {
      $prefix .= "../";
      $offset = $pos + 1;
    }
    return $prefix;
  }

  static function add(string $url, ?string $prefix): string {
    if ($prefix === null) return $url;
    if (preg_match('/^(\/)|(https?:\/\/)/', $url)) {
      # l'url est de la forme /ANY ou http[s]://ANY
      # url absolue, laisser en l'état
    } elseif (str::starts_with("./", $url)) {
      # l'url est de la forme ./ANY
      # url relative
      $url = $prefix.substr($url, 2);
    } elseif (str::starts_with("__PREFIX__/", $url)) {
      # l'url est de la forme __PREFIX__/ANY
      # compatibilité: url exprimée par rapport au préfixe
      $url = $prefix.substr($url, 11);
    } else {
      # par défaut, l'url est exprimée par rapport au préfixe
      $url = $prefix.$url;
    }
    return $url;
  }

  #############################################################################
  # exemple d'implémentation pour utiliser la valeur calculée de prefix

  /**
   * @var string chemin du script correspondant à la page courante, depuis la
   * racine de l'application e.g. index.php, ou null s'il faut utiliser la
   * valeur par défaut
   */
  protected static $self;

  /** @var string */
  protected static $prefix;

  static final function set_self(?string $self) {
    if ($self === null) $self = self::get_default_self();
    self::$self = $self;
    self::$prefix = self::compute($self);
  }

  /**
   * construire un url avec les paramètres spécifiés. le cas échéant, corriger
   * l'url en fonction de $self
   *
   * cette méthode sera utilisée notamment pour des liens vers des resources
   * statiques
   */
  static final function resu(string $url, ...$params): string {
    return html::bu(self::add($url, self::$prefix), ...$params);
  }

  /**
   * construire un url vers le composant destination avec les paramètres
   * spécifiés. le cas échéant, corriger l'url en fonction de $self
   *
   * @param string|IPage $dest composant destination
   */
  static final function dynu($dest, ...$params): string {
    $url = $dest; #XXX $url = route::get_path($dest);
    return html::bu(self::add($url, self::$prefix), ...$params);
  }

  const BU_STATIC_PARTS = ["scheme", "host", "port", "query", "fragment"];

  /**
   * méthode générique pour construire une url. utiliser des heuristiques pour
   * déterminer si la destination est une url statique ou si c'est un composant
   */
  static final function bu($dest, ...$params): string {
    $dyn = true;
    if (is_string($dest)) {
      # heuristiques pour déterminer si c'est une url statique
      $parts = parse_url($dest);
      foreach (self::BU_STATIC_PARTS as $part) {
        if (A::has($parts, $part)) {
          # y a-t-il au moins une partie d'une url statique?
          $dyn = false;
          break;
        }
      }
      if ($dyn) {
        # s'il n'y a qu'un chemin, tester si ça ressemble à une url
        $path = A::get($parts, "path", "");
        if ($path === "" || $path === ".") $dyn = false;
        elseif (strpos($path, "/")) $dyn = false;
        elseif (preg_match('/\.(php|html)\b/', $path)) $dyn = false;
      }
    }
    if ($dyn) {
      # tenir compte du préfixe, puisque les chemins des composants sont
      # toujours exprimés depuis la racine
      $url = $dest; #XXX $url = route::get_path($dest);
      return html::bu(self::add($url, self::$prefix), ...$params);
    } else {
      # ne pas corriger une url statique: on fait confiance à l'utilisateur pour
      # utiliser des chemins relatifs
      return html::bu($dest, ...$params);
    }
  }
}
