<?php

namespace Hubleto\Framework;

/**
 * Various helper functions.
 */
class Helper
{

  public function __construct()
  {
  }

  /** Function serves as a delete funtion for the Tag Input
   * @param array $recordTags The IDs of the saved tags
   * @param string $crossTagModelName The string of the model from which the tag entries will be deleted
   * @param string $lookupColumnName Name of the lookup column in the cross tag model
   * @param int $lookupId The id of the lookup
   */
  public function deleteTags(
    array $recordTagsIds,
    mixed $mCrossTag,
    string $lookupColumnName,
    int $lookupId
  ): void {
    $existingTagsIds = $mCrossTag->record
      ->where($lookupColumnName, $lookupId)
      ->pluck("id")
      ->toArray()
    ;
    $differences = array_diff($existingTagsIds, $recordTagsIds);
    $mCrossTag->record
      ->whereIn("id", $differences)
      ->where($lookupColumnName, $lookupId)
      ->delete()
    ;
  }

  static $loadUrlError = '';

  /**
   * Minifies HTML
   *
   * @param  string $html Input HTML
   * @return string Minified HTML
   */
  public static function minifyHtml($html) {
    $search = [
      '/\>[^\S ]+/s',     // strip whitespaces after tags, except space
      '/[^\S ]+\</s',     // strip whitespaces before tags, except space
      '/(\s)+/s',         // shorten multiple whitespace sequences
      '/<!--(.|\s)*?-->/' // Remove HTML comments
    ];

    $replace = ['>', '<', '\\1', ''];

    return preg_replace($search, $replace, $html);
  }

  /**
   * Load content of remote URL using PHP's CURL library.
   *
   * @param  string $url URL to be loaded
   * @param  array $post Array of POST values to be posted to the request
   * @return string Loaded content of remote URL
   */
  public static function loadUrl($url, $post = []) {

    self::$loadUrlError = '';

    if (is_callable('curl_init')) {
      $ch = curl_init();

      curl_setopt($ch, CURLOPT_URL, $url);
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
      curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_POST, count($post));
      curl_setopt($ch, CURLOPT_POSTFIELDS, $post);
      curl_setopt($ch, CURLOPT_TIMEOUT, 5);
      curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
      curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
      curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024 * 1024 * 1024 * 10);
      curl_setopt($ch, CURLOPT_SSLVERSION, CURL_SSLVERSION_MAX_TLSv1_3);
      curl_setopt($ch, CURLOPT_TIMEOUT, 1000);

      $html = curl_exec($ch);
      self::$loadUrlError = curl_error($ch);

      // curl_close($ch);
    } else {
      $error = 'CURL is not available';
    }

    return '' == self::$loadUrlError ? $html : false;
  }

  /**
   * Removes special characters from string
   *
   * @param  string $string Original string
   * @return string String with removed special characters
   */
  public static function rmspecialchars($string) {
    $from = ['!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '{', '}', '[', ']', ':', '|', ';', "'", '\\', ',', '.', '/', '<', '>', '?'];
    foreach ($from as $char) {
      $string = str_replace($char, '', $string);
    }

    return $string;
  }

  /**
   * Removes punctuation characters from string
   *
   * @param  string $string Original string
   * @return string String with removed punctuation characters
   */
  public static function rmdiacritic($string) {
    $from = ['ŕ', 'ě', 'š', 'č', 'ř', 'š', 'ž', 'ť', 'ď', 'ľ', 'ĺ', 'ý', 'á', 'í', 'ä', 'é', 'ú', 'ü', 'ö', 'ô', 'ó', 'ň', 'Ě', 'Š', 'Č', 'Ř', 'Š', 'Ť', 'Ď', 'Ľ', 'Ĺ', 'Ž', 'Ý', 'Á', 'Í', 'É', 'Ú', 'Ü', 'Ó', 'Ó', 'Ň'];
    $to = ['r', 'e', 's', 'c', 'r', 's', 'z', 't', 'd', 'l', 'l', 'y', 'a', 'i', 'a', 'e', 'u', 'u', 'o', 'o', 'o', 'n', 'E', 'S', 'C', 'R', 'S', 'T', 'D', 'L', 'L', 'Z', 'Y', 'A', 'I', 'E', 'U', 'U', 'O', 'O', 'N'];

    return str_replace($from, $to, $string);
  }

  /**
   * Convert string with to URL-compatible string
   *
   * @param  string $string Original string
   * @param  bool $replaceSlashes If TRUE, slashes are replaced with hyphenation
   * @return string URL-compatible string
   */
  public static function str2url(string $string, bool $replaceSlashes = TRUE) {
    if ($replaceSlashes) {
      $string = str_replace('/', '-', $string);
    }

    $string = str_replace('_', '-', $string);
    $string = preg_replace('/ |^(a-z0-9)/', '-', strtolower(self::rmspecialchars(self::rmdiacritic($string))));

    $string = preg_replace('/[^(\x20-\x7F)]*/', '', $string);
    $string = preg_replace('/[^(\-a-z0-9)]*/', '', $string);
    $string = trim($string, '-');

    while (strpos($string, '--')) {
      $string = str_replace('--', '-', $string);
    }

    return $string;
  }

  public static function str2uid($string, $replaceSlashes = TRUE) {
    return str_replace("-", "_", self::str2url($string, $replaceSlashes));
  }

  public static function generateUuidV4(): string
  {
    $data = random_bytes(16);

    $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100
    $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10

    return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
  }

  /**
   * Generates random password
   *
   * @return string Generated random password
   */
  public static function randomPassword() {
    $alphabet = [
      '0' => 'abcdefghijklmnopqrstuvwxyz',
      '1' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZ',
      '2' => '1234567890',
      '3' => '_!*$^',
    ];

    $patterns = [
      '12231110130112',
      '1211132101122310',
      '1122312130113010',
      '131213011220',
    ];

    $pattern = $patterns[rand(0, count($patterns) - 1)];

    $pass = "";
    for ($i = 0; $i < strlen($pattern) - 1; $i++) {
      $charset = $alphabet[$pattern[$i]];
      $pass .= $charset[rand(0, strlen($charset) - 1)];
    }

    return $pass;
  }

  public static function scanDirRecursively($dir) : array {
    $result = [];
    foreach(scandir($dir) as $filename) {
      if ($filename[0] === '.') continue;
      $filePath = $dir . '/' . $filename;
      if (is_dir($filePath)) {
        foreach (self::scanDirRecursively($filePath) as $childFilename) {
          $result[] = $filename . '/' . $childFilename;
        }
      } else {
        $result[] = $filename;
      }
    }
    return $result;
  }

  /**
   * Funkcia slúži na rekurzívny merge viacúrovňových polí.
   *
   * @version 1
   *
   * @internal
   * @param array $orig Pole pôvodných parametrov
   * @param array $new Pole parametrov, ktoré aktualizujú a dopĺňajú $params
   *
   * @return array Zmergované výsledné pole
   */
  public static function arrayMergeRecursively(array $orig, array $new): array
  {
    foreach ($new as $key => $val) {
      if (is_array($val)) {
        $orig[$key] = self::arrayMergeRecursively(
          $orig[$key] ?? [],
          $val
        );
      } else {
        $orig[$key] = $val;
      }
    }

    return $orig;
  }


  public static function capitalizeFirstLetter(string $s) {
    return \strtoupper(substr($s, 0, 1)) . substr($s, 1);
  }

  public static function kebabToCamel(string $s) {
    $str = str_replace('-', '', ucwords($s, '-'));
    $str[0] = strtolower($str[0]);
    return $str;
  }

  public static function kebabToPascal(string $s) {
    return self::capitalizeFirstLetter(self::kebabToCamel($s));
  }

  public static function camelToKebab(string $s) {
    return strtolower(preg_replace("/[A-Z]/", "-$0", $s));
  }

  public static function pascalToKebab(string $s) {
    return trim(self::camelToKebab($s), '-');
  }

  public static function pluck(string $key, array $data): array
  {
    $pluck = [];
    foreach ($data as $value) {
      if (isset($value['id'])) $pluck[] = $value['id'];
    }
    return $pluck;
  }

  public static function keyBy(string $key, array $data): array {
    $result = [];
    $keys = explode('.', $key);

    foreach ($data as $item) {

      $tmp = &$result;
      foreach ($keys as $key) {
        if (!isset($tmp[$item[$key]])) $tmp[$item[$key]] = [];
        $tmp = &$tmp[$item[$key]];
      }
      $tmp = $item;
    }

    return $result;
  }

  public static function groupBy(string $key, array $data): array {
    $result = [];

    foreach ($data as $item) {
      $result[$item[$key]][] = $item;
    }

    return $result;
  }

  public static function encrypt(string $value, string $seed = '', $force = false) {
    $__APP__ = \Hubleto\Framework\Loader::getGlobalApp();
    if ($force || ($__APP__->config()->getAsBool('encryptRecordIds'))) {
      if (empty($seed)) $seed = $__APP__->sessionManager()->getSalt();
      return base64_encode(@openssl_encrypt($value, 'AES-256-CBC', $seed, 0, substr($seed, 0, 16)));
    } else {
      return $value;
    }
  }

  public static function decrypt(string $value, string $seed = '', $force = false) {
    $__APP__ = \Hubleto\Framework\Loader::getGlobalApp();
    if ($force || ($__APP__->config()->getAsBool('encryptRecordIds'))) {
      if (empty($seed)) $seed = $__APP__->sessionManager()->getSalt();
      return @openssl_decrypt(base64_decode($value), 'AES-256-CBC', $seed, 0, substr($seed, 0, 16));
    } else {
      return $value;
    }
  }
}
