<?php
namespace GlpiPlugin\Wbstore;

if (!defined('GLPI_ROOT')) {
   die("Sorry. You can't access this file directly");
}

class Utils {

   public static function uuidv4(): string {
      $data = random_bytes(16);
      $data[6] = chr((ord($data[6]) & 0x0f) | 0x40);
      $data[8] = chr((ord($data[8]) & 0x3f) | 0x80);
      return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
   }

   public static function currentDomain(): string {
      // Use GLPI base URL if available, fallback to HTTP_HOST
      $host = $_SERVER['HTTP_HOST'] ?? '';
      $host = preg_replace('/:\d+$/', '', $host);
      return strtolower(trim($host));
   }

   public static function pluginsRoot(): string {
      // GLPI 10 plugins directory
      // We try in order:
      // 1) GLPI_MARKETPLACE_DIR/plugins (if defined)
      // 2) GLPI_ROOT/plugins
      if (defined('GLPI_MARKETPLACE_DIR')) {
         $p = rtrim(GLPI_MARKETPLACE_DIR, '/\\') . DIRECTORY_SEPARATOR . 'plugins';
         if (is_dir($p)) {
            return $p;
         }
      }
      return rtrim(GLPI_ROOT, '/\\') . DIRECTORY_SEPARATOR . 'plugins';
   }

   public static function tmpFile(string $prefix, string $ext = 'tmp'): string {
      $dir = defined('GLPI_TMP_DIR') ? GLPI_TMP_DIR : sys_get_temp_dir();
      if (!is_dir($dir)) {
         @mkdir($dir, 0775, true);
      }
      $name = $prefix . '_' . bin2hex(random_bytes(8)) . '.' . $ext;
      return rtrim($dir, '/\\') . DIRECTORY_SEPARATOR . $name;
   }

   public static function sha256File(string $path): string {
      return hash_file('sha256', $path);
   }

   public static function jsonDecode(string $body): array {
      $data = json_decode($body, true);
      if (!is_array($data)) {
         return [];
      }
      return $data;
   }

   public static function ensureDir(string $dir): bool {
      if (is_dir($dir)) {
         return is_writable($dir);
      }
      if (@mkdir($dir, 0775, true)) {
         return true;
      }
      return false;
   }

   public static function rrmdir(string $dir): void {
      if (!is_dir($dir)) return;
      $items = new \RecursiveIteratorIterator(
         new \RecursiveDirectoryIterator($dir, \FilesystemIterator::SKIP_DOTS),
         \RecursiveIteratorIterator::CHILD_FIRST
      );
      foreach ($items as $item) {
         if ($item->isDir()) {
            @rmdir($item->getPathname());
         } else {
            @unlink($item->getPathname());
         }
      }
      @rmdir($dir);
   }

   /**
    * Verifica se um plugin está instalado/ativado pelo diretório.
    * (Sem depender de banco, mas tenta usar a classe Plugin quando disponível.)
    */
   public static function pluginStatus(string $pluginDir): array {
      $pluginDir = trim($pluginDir);
      if ($pluginDir === '') {
         return ['installed' => false, 'activated' => false];
      }

      $root = rtrim(self::pluginsRoot(), '/\\') . DIRECTORY_SEPARATOR . $pluginDir;
      $installed = is_dir($root) && is_file($root . DIRECTORY_SEPARATOR . 'setup.php');

      $activated = false;
      if ($installed && class_exists('Plugin') && method_exists('Plugin', 'isActivated')) {
         try {
            $activated = (bool)\Plugin::isActivated($pluginDir);
         } catch (\Throwable $e) {
            $activated = false;
         }
      }

      return ['installed' => $installed, 'activated' => $activated];
   }


   public static function glpiUrl(): string {
      global $CFG_GLPI;
      if (isset($CFG_GLPI['url_base']) && is_string($CFG_GLPI['url_base']) && $CFG_GLPI['url_base'] !== '') {
         return (string)$CFG_GLPI['url_base'];
      }
      // fallback
      $proto = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
      $host = $_SERVER['HTTP_HOST'] ?? '';
      $base = rtrim(dirname($_SERVER['SCRIPT_NAME'] ?? '/'), '/\\');
      return $proto . '://' . $host . $base;
   }

   public static function httpPostJson(string $url, array $payload, bool $verifySsl = true, int $timeout = 20): array {
      $body = json_encode($payload, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
      if (!is_string($body)) {
         $body = '{}';
      }

      // cURL
      if (function_exists('curl_init')) {
         $ch = curl_init();
         curl_setopt($ch, CURLOPT_URL, $url);
         curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
         curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
         curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, min(10, $timeout));
         curl_setopt($ch, CURLOPT_POST, true);
         curl_setopt($ch, CURLOPT_POSTFIELDS, $body);
         curl_setopt($ch, CURLOPT_HTTPHEADER, [
            'Content-Type: application/json',
            'Accept: application/json'
         ]);
         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, $verifySsl);
         curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, $verifySsl ? 2 : 0);
         $resp = curl_exec($ch);
         $err  = curl_error($ch);
         $code = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
         curl_close($ch);
         return ['code' => $code, 'body' => (string)$resp, 'error' => (string)$err];
      }

      // fallback
      $opts = [
         'http' => [
            'method'  => 'POST',
            'header'  => "Content-Type: application/json\r\nAccept: application/json\r\n",
            'content' => $body,
            'timeout' => $timeout,
         ],
      ];
      $ctx = stream_context_create($opts);
      $resp = @file_get_contents($url, false, $ctx);
      $code = 0;
      if (isset($http_response_header) && is_array($http_response_header)) {
         foreach ($http_response_header as $h) {
            if (preg_match('#^HTTP/\S+\s+(\d{3})#', $h, $m)) {
               $code = (int)$m[1];
               break;
            }
         }
      }
      return ['code' => $code, 'body' => (string)$resp, 'error' => ''];
   }

   /**
    * Monta a URL do iframe de avisos (renderizado no Portal/Store) para esta instalação.
    *
    * Padrão: {store_url}/wbstore/avisos
    * Override (opcional): config notices_iframe_url
    */
   
   /**
    * Gera e persiste um portal_token local (usado para iframes/avisos e validações leves no Portal),
    * evitando depender de token externo. É estável e assinado pela license_key.
    */
   public static function ensurePortalToken(): string {
      $current = trim((string)Config::get('portal_token', ''));
      $iid = (string)Config::get('instance_id', '');
      $dom = self::currentDomain();
      $lk  = (string)Config::get('license_key', '');
      $key  = ($lk !== '' ? $lk : 'wbstore');

      // Token determinístico por instância/domínio (evita "token inválido" por troca/limpeza de cache)
      $desired = substr(hash_hmac('sha256', $iid . '|' . $dom, $key), 0, 40);

      if ($current === '' || $current !== $desired) {
         Config::set(['portal_token' => $desired]);
         return $desired;
      }
      return $current;
   }

   /**
    * Assina um payload (HMAC) usando a license_key como segredo.
    */
   public static function hmac(string $payload): string {
      $lk = (string)Config::get('license_key', '');
      $key = ($lk !== '' ? $lk : 'wbstore');
      return hash_hmac('sha256', $payload, $key);
   }

public static function portalNoticesIframeUrl(array $extraParams = []): string {
      $base = trim((string)Config::get('notices_iframe_url', ''));
      if ($base === '') {
         $store = rtrim((string)Config::get('store_url', ''), '/');
         if ($store === '') {
            return '';
         }
         $base = $store . '/wbstore/avisos';
      }

      $params = [
         'instance_id' => (string)Config::get('instance_id', ''),
         'license_key' => (string)Config::get('license_key', ''),
         'domain'      => self::currentDomain(),
         // cache-bust básico
         'ts'          => time(),
      ];

      // Token de autenticação do Portal (avisos via iframe)
      // IMPORTANT: portal_token é gerado localmente no GLPI e pode ser registrado no Portal via sync.
      $portalToken = self::ensurePortalToken();
      if ($portalToken !== '') {
         // Portal aceita portal_token e (fallback) token
         $params['portal_token'] = $portalToken;
         $params['token'] = $portalToken;
      }

      // Não adiciona token de API no iframe (evita vazar segredo em URL).

      if (!empty($extraParams)) {
         foreach ($extraParams as $k => $v) {
            $params[(string)$k] = (string)$v;
         }
      }

      $sep = (strpos($base, '?') === false) ? '?' : '&';
      return $base . $sep . http_build_query($params);
   }

}