<?php
/**
 * @intranet-file base/plugin_builder/_templates/plugin_installer.tpl.php
 * @intranet-file-version 2026.03.28.1
 * @intranet-file-date 2026-03-28
 * @intranet-file-owner CSIF Intranet
 */
declare(strict_types=1);

$PLUGIN_META_JSON = <<<'JSON'
__PLUGIN_META_JSON__
JSON;

$PLUGIN_META = json_decode($PLUGIN_META_JSON, true);
if (!is_array($PLUGIN_META)) {
  throw new RuntimeException("PLUGIN_META invalido");
}

function plugin_h(string $s): string
{
  return htmlspecialchars($s, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function plugin_normalize_roles($value, array $fallback = ['administradores']): array
{
  if (function_exists('intranet_roles_from_post')) {
    return intranet_roles_from_post($value, $fallback);
  }

  if (is_string($value)) {
    $raw = trim($value);
    if ($raw === '') {
      return $fallback;
    }
    $decoded = json_decode($raw, true);
    $value = (json_last_error() === JSON_ERROR_NONE && is_array($decoded))
      ? $decoded
      : preg_split('/[\s,;]+/', $raw, -1, PREG_SPLIT_NO_EMPTY);
  }

  if (!is_array($value)) {
    return $fallback;
  }

  $roles = [];
  foreach ($value as $role) {
    if (!is_string($role) && !is_numeric($role)) {
      continue;
    }
    $role = trim((string) $role);
    if ($role !== '') {
      $roles[] = $role;
    }
  }

  return array_values(array_unique($roles)) ?: $fallback;
}

function plugin_available_roles(?PDO $pdo, array $selected): array
{
  if (function_exists('intranet_available_roles')) {
    return intranet_available_roles($pdo, $selected);
  }

  $roles = [
    'administradores' => true,
    'app_responsables' => true,
  ];
  foreach ($selected as $role) {
    $role = trim((string) $role);
    if ($role !== '') {
      $roles[$role] = true;
    }
  }
  foreach (is_array($_SESSION['roles'] ?? null) ? $_SESSION['roles'] : [] as $role) {
    $role = trim((string) $role);
    if ($role !== '') {
      $roles[$role] = true;
    }
  }

  try {
    $rs = $pdo->query("SELECT roles_json FROM intranet_apps WHERE roles_json IS NOT NULL AND roles_json <> ''");
    if ($rs !== false) {
      while ($row = $rs->fetch(PDO::FETCH_ASSOC)) {
        foreach (plugin_normalize_roles((string) ($row['roles_json'] ?? ''), []) as $role) {
          $roles[$role] = true;
        }
      }
    }
  } catch (Throwable $e) {
  }

  $out = array_keys($roles);
  sort($out, SORT_NATURAL | SORT_FLAG_CASE);
  return $out;
}

function plugin_connect_admin_pdo(array $adminDb): PDO
{
  $host = trim((string) ($adminDb['host'] ?? 'localhost'));
  $port = (int) ($adminDb['port'] ?? 3306);
  $user = trim((string) ($adminDb['user'] ?? ''));
  $pass = (string) ($adminDb['pass'] ?? '');

  if ($user === '') {
    throw new RuntimeException('Debes indicar el usuario administrador de MySQL para ejecutar los SQL con privilegios elevados.');
  }

  $dsn = "mysql:host={$host};port={$port};charset=utf8mb4";
  return new PDO($dsn, $user, $pass, [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
  ]);
}

function plugin_sorted_sql_scripts(array $scripts): array
{
  $out = [];
  foreach ($scripts as $item) {
    if (!is_array($item) || empty($item['file']) || empty($item['enabled'])) {
      continue;
    }
    $out[] = [
      'file' => (string) $item['file'],
      'enabled' => !empty($item['enabled']) ? 1 : 0,
      'order' => (int) ($item['order'] ?? 50),
      'stage' => (string) ($item['stage'] ?? 'update'),
      'mode' => (string) ($item['mode'] ?? 'auto'),
    ];
  }

  usort($out, static function (array $a, array $b): int {
    $cmp = ((int) $a['order']) <=> ((int) $b['order']);
    if ($cmp !== 0) {
      return $cmp;
    }
    return strcasecmp((string) $a['file'], (string) $b['file']);
  });

  return $out;
}

function plugin_sql_file_path(string $pluginRoot, string $appFolder, string $file): string
{
  $baseDir = rtrim(str_replace('\\', '/', $pluginRoot), '/') . '/apps/' . trim(str_replace('\\', '/', $appFolder), '/');
  $baseReal = realpath($baseDir);
  $relative = ltrim(str_replace('\\', '/', $file), '/');
  if ($baseReal === false) {
    return $baseDir . '/' . $relative;
  }

  return rtrim(str_replace('\\', '/', $baseReal), '/') . '/' . $relative;
}

function plugin_read_sql_script(string $pluginRoot, string $appFolder, array $script): array
{
  $path = plugin_sql_file_path($pluginRoot, $appFolder, (string) $script['file']);
  $exists = is_file($path);
  $sql = $exists ? (string) file_get_contents($path) : '';

  return [
    'file' => (string) $script['file'],
    'path' => $path,
    'exists' => $exists,
    'order' => (int) ($script['order'] ?? 50),
    'stage' => (string) ($script['stage'] ?? 'update'),
    'mode' => (string) ($script['mode'] ?? 'auto'),
    'sql' => $sql,
  ];
}

function plugin_split_sql_statements(string $sql): array
{
  $statements = [];
  $buffer = '';
  $length = strlen($sql);
  $inSingle = false;
  $inDouble = false;
  $inBacktick = false;
  $inLineComment = false;
  $inBlockComment = false;

  for ($i = 0; $i < $length; $i++) {
    $char = $sql[$i];
    $next = ($i + 1 < $length) ? $sql[$i + 1] : '';

    if ($inLineComment) {
      $buffer .= $char;
      if ($char === "\n") {
        $inLineComment = false;
      }
      continue;
    }

    if ($inBlockComment) {
      $buffer .= $char;
      if ($char === '*' && $next === '/') {
        $buffer .= $next;
        $i++;
        $inBlockComment = false;
      }
      continue;
    }

    if (!$inSingle && !$inDouble && !$inBacktick) {
      if ($char === '-' && $next === '-' && (($i + 2 >= $length) || ctype_space($sql[$i + 2]))) {
        $buffer .= $char . $next;
        $i++;
        $inLineComment = true;
        continue;
      }
      if ($char === '#') {
        $buffer .= $char;
        $inLineComment = true;
        continue;
      }
      if ($char === '/' && $next === '*') {
        $buffer .= $char . $next;
        $i++;
        $inBlockComment = true;
        continue;
      }
    }

    if ($char === "'" && !$inDouble && !$inBacktick) {
      $escaped = ($i > 0 && $sql[$i - 1] === '\\');
      if (!$escaped) {
        $inSingle = !$inSingle;
      }
      $buffer .= $char;
      continue;
    }

    if ($char === '"' && !$inSingle && !$inBacktick) {
      $escaped = ($i > 0 && $sql[$i - 1] === '\\');
      if (!$escaped) {
        $inDouble = !$inDouble;
      }
      $buffer .= $char;
      continue;
    }

    if ($char === '`' && !$inSingle && !$inDouble) {
      $inBacktick = !$inBacktick;
      $buffer .= $char;
      continue;
    }

    if ($char === ';' && !$inSingle && !$inDouble && !$inBacktick) {
      $statement = trim($buffer);
      if ($statement !== '') {
        $statements[] = $statement;
      }
      $buffer = '';
      continue;
    }

    $buffer .= $char;
  }

  $tail = trim($buffer);
  if ($tail !== '') {
    $statements[] = $tail;
  }

  return $statements;
}

function plugin_execute_sql_script(PDO $pdo, string $sql): int
{
  $count = 0;
  foreach (plugin_split_sql_statements($sql) as $statement) {
    $trimmed = trim($statement);
    if ($trimmed === '' || preg_match('/^(--|#)/', $trimmed)) {
      continue;
    }
    $pdo->exec($statement);
    $count++;
  }

  return $count;
}

function plugin_apply_sql_scripts(PDO $pdo, array $ctx = []): array
{
  global $PLUGIN_META;

  $pluginRoot = (string) ($ctx['plugin_root'] ?? realpath(__DIR__ . '/..'));
  $appFolder = (string) ($ctx['app_folder'] ?? ($PLUGIN_META['app_folder'] ?? ''));
  $strategy = (string) ($ctx['sql_strategy'] ?? 'auto');
  $adminDb = is_array($ctx['admin_db'] ?? null) ? $ctx['admin_db'] : [];
  $scripts = plugin_sorted_sql_scripts($PLUGIN_META['sql_scripts'] ?? []);

  $executed = [];
  $manual = [];
  $errors = [];
  $executionPdo = $pdo;
  $connectionLabel = 'usuario de aplicacion';

  if ($strategy === 'admin' && !empty($scripts)) {
    try {
      $executionPdo = plugin_connect_admin_pdo($adminDb);
      $connectionLabel = 'usuario administrador';
    } catch (Throwable $e) {
      $errors[] = $e->getMessage();
      return [
        'ok' => false,
        'executed' => [],
        'manual' => [],
        'errors' => $errors,
        'connection' => $connectionLabel,
      ];
    }
  }

  foreach ($scripts as $script) {
    $resolved = plugin_read_sql_script($pluginRoot, $appFolder, $script);
    if (!$resolved['exists']) {
      $errors[] = 'No se encontro el SQL: ' . $resolved['file'];
      continue;
    }

    if ($strategy === 'skip') {
      continue;
    }

    if ($strategy === 'show') {
      $manual[] = $resolved;
      continue;
    }

    if ($resolved['mode'] === 'manual') {
      $manual[] = $resolved;
      continue;
    }

    try {
      $statementCount = plugin_execute_sql_script($executionPdo, (string) $resolved['sql']);
      $resolved['statement_count'] = $statementCount;
      $resolved['connection'] = $connectionLabel;
      $executed[] = $resolved;
    } catch (Throwable $e) {
      $resolved['error'] = $e->getMessage();
      $errors[] = 'Fallo al ejecutar ' . $resolved['file'] . ': ' . $e->getMessage();
      break;
    }
  }

  return [
    'ok' => empty($errors),
    'executed' => $executed,
    'manual' => $manual,
    'errors' => $errors,
    'connection' => $connectionLabel,
  ];
}

function plugin_normalize_relative_path(string $path): string
{
  $path = trim(str_replace('\\', '/', $path));
  $path = preg_replace('~/+~', '/', $path);
  $path = ltrim((string) $path, '/');
  if ($path === '' || preg_match('~(^|/)\.\.(/|$)~', $path)) {
    return '';
  }
  return $path;
}

function plugin_normalize_path_segment($value): string
{
  $value = trim(str_replace('\\', '/', (string) $value));
  $value = trim($value, '/');
  if ($value === '' || strpos($value, '..') !== false) {
    return '';
  }
  if (strpos($value, '/') !== false) {
    $value = basename($value);
  }
  $value = trim((string) preg_replace('~[^A-Za-z0-9._-]+~', '-', $value), '.-');
  return $value;
}

function plugin_meta_plugin_id(array $meta, array $ctx = []): string
{
  foreach ([
    $ctx['plugin_id'] ?? '',
    $meta['app_id'] ?? '',
    $meta['id'] ?? '',
    $meta['plugin_id'] ?? '',
    $meta['app']['app_id'] ?? '',
  ] as $candidate) {
    $value = plugin_normalize_path_segment($candidate);
    if ($value !== '') {
      return $value;
    }
  }

  $pluginRoot = (string) ($ctx['plugin_root'] ?? realpath(__DIR__ . '/..'));
  return plugin_normalize_path_segment(basename(str_replace('\\', '/', $pluginRoot)));
}

function plugin_meta_app_folder(array $meta, array $ctx = []): string
{
  foreach ([
    $ctx['app_folder'] ?? '',
    $meta['app_folder'] ?? '',
    $meta['app']['folder'] ?? '',
  ] as $candidate) {
    $value = plugin_normalize_path_segment($candidate);
    if ($value !== '') {
      return $value;
    }
  }

  $url = trim(str_replace('\\', '/', (string) ($meta['url'] ?? ($meta['app']['url'] ?? ''))));
  if (preg_match('~/?apps/([^/]+)/~', $url, $m)) {
    return plugin_normalize_path_segment($m[1]);
  }

  return '';
}

function plugin_normalize_link_target(string $path): string
{
  $path = trim(str_replace('\\', '/', $path));
  $path = preg_replace('~/+~', '/', $path);
  return rtrim((string) $path, '/');
}

function plugin_scheduled_tasks(array $tasks): array
{
  $out = [];
  foreach ($tasks as $task) {
    if (!is_array($task) || empty($task['script']) || empty($task['schedule']) || empty($task['enabled'])) {
      continue;
    }

    $script = plugin_normalize_relative_path((string) ($task['script'] ?? ''));
    if ($script === '') {
      continue;
    }

    $mode = strtolower(trim((string) ($task['mode'] ?? 'manual')));
    if ($mode !== 'managed') {
      $mode = 'manual';
    }

    $out[] = [
      'id' => (string) ($task['id'] ?? pathinfo($script, PATHINFO_FILENAME)),
      'name' => trim((string) ($task['name'] ?? pathinfo($script, PATHINFO_FILENAME))),
      'description' => trim((string) ($task['description'] ?? '')),
      'script' => $script,
      'schedule' => trim((string) ($task['schedule'] ?? '')),
      'mode' => $mode,
      'php_bin' => trim((string) ($task['php_bin'] ?? '/usr/bin/php')) ?: '/usr/bin/php',
      'args' => trim((string) ($task['args'] ?? '')),
      'managed_path' => plugin_normalize_relative_path((string) ($task['managed_path'] ?? '')),
    ];
  }

  return $out;
}

function plugin_support_scripts(array $scripts): array
{
  $out = [];
  foreach ($scripts as $script) {
    if (!is_array($script)) {
      continue;
    }

    $packagePath = plugin_normalize_relative_path((string) ($script['package_path'] ?? ''));
    if ($packagePath === '') {
      continue;
    }

    $out[] = [
      'source_path' => plugin_normalize_relative_path((string) ($script['source_path'] ?? '')),
      'package_path' => $packagePath,
      'description' => trim((string) ($script['description'] ?? '')),
      'instructions' => trim((string) ($script['instructions'] ?? '')),
    ];
  }

  return $out;
}

function plugin_app_script_file(string $pluginRoot, string $appFolder, string $relativeFile): array
{
  $relativeFile = plugin_normalize_relative_path($relativeFile);
  $baseDir = rtrim(str_replace('\\', '/', $pluginRoot), '/') . '/apps/' . trim(str_replace('\\', '/', $appFolder), '/');
  $baseReal = realpath($baseDir);
  $path = ($baseReal !== false ? rtrim(str_replace('\\', '/', $baseReal), '/') : $baseDir) . '/' . $relativeFile;
  $exists = $relativeFile !== '' && is_file($path);

  return [
    'relative' => $relativeFile,
    'path' => $path,
    'exists' => $exists,
    'content' => $exists ? (string) file_get_contents($path) : '',
  ];
}

function plugin_secure_directory(string $dir): void
{
  if ($dir === '') {
    return;
  }

  @mkdir($dir, 0775, true);

  $htaccessPath = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR . '.htaccess';
  if (!is_file($htaccessPath)) {
    @file_put_contents($htaccessPath, "Require all denied\nDeny from all\n");
  }

  $indexPath = rtrim($dir, '/\\') . DIRECTORY_SEPARATOR . 'index.html';
  if (!is_file($indexPath)) {
    @file_put_contents($indexPath, '');
  }
}

function plugin_private_cron_root(string $publicRoot, string $pluginId): string
{
  $safePluginId = preg_replace('~[^A-Za-z0-9._-]+~', '-', $pluginId);
  return rtrim(str_replace('\\', '/', $publicRoot), '/') . '/var/cron/' . $safePluginId;
}

function plugin_cron_command(array $task, string $scriptPath): string
{
  $parts = [
    escapeshellarg((string) ($task['php_bin'] ?? '/usr/bin/php')),
    escapeshellarg($scriptPath),
  ];

  $args = trim((string) ($task['args'] ?? ''));
  if ($args !== '') {
    $parts[] = $args;
  }

  return implode(' ', $parts);
}

function plugin_prepare_scheduled_tasks(array $ctx = []): array
{
  global $PLUGIN_META;

  $publicRoot = (string) ($ctx['public_root'] ?? realpath(__DIR__ . '/../../..'));
  $pluginRoot = (string) ($ctx['plugin_root'] ?? realpath(__DIR__ . '/..'));
  $pluginId = plugin_meta_plugin_id($PLUGIN_META, $ctx);
  $appFolder = plugin_meta_app_folder($PLUGIN_META, $ctx);
  $tasks = plugin_scheduled_tasks($PLUGIN_META['scheduled_tasks'] ?? []);

  if ($appFolder === '' || empty($tasks)) {
    return [
      'ok' => true,
      'tasks' => [],
      'errors' => [],
      'private_root' => '',
    ];
  }

  $publicRoot = str_replace('\\', '/', $publicRoot);
  $privateRoot = plugin_private_cron_root($publicRoot, $pluginId);
  $errors = [];
  $items = [];

  foreach ($tasks as $task) {
    $resolved = plugin_app_script_file($pluginRoot, $appFolder, (string) $task['script']);
    $item = $task + [
      'source_path' => (string) ($resolved['path'] ?? ''),
      'target_path' => '',
      'cron_line' => '',
      'command' => '',
      'code' => (string) ($resolved['content'] ?? ''),
      'deployed' => false,
      'error' => '',
    ];

    if (empty($resolved['exists'])) {
      $item['error'] = 'No se encontro el script dentro del plugin: ' . (string) $task['script'];
      $errors[] = $item['error'];
      $items[] = $item;
      continue;
    }

    if (($task['mode'] ?? 'manual') === 'managed') {
      $targetRel = plugin_normalize_relative_path((string) ($task['managed_path'] ?? ''));
      if ($targetRel === '') {
        $targetRel = basename((string) $resolved['relative']);
      }

      $targetPath = rtrim($privateRoot, '/') . '/' . $targetRel;
      $targetDir = dirname($targetPath);
      plugin_secure_directory(rtrim($publicRoot, '/') . '/var');
      plugin_secure_directory(rtrim($publicRoot, '/') . '/var/cron');
      plugin_secure_directory($privateRoot);
      plugin_secure_directory($targetDir);

      if (@file_put_contents($targetPath, (string) $resolved['content']) === false) {
        $item['error'] = 'No se pudo desplegar el script privado: ' . $targetPath;
        $errors[] = $item['error'];
      } else {
        @chmod($targetPath, 0640);
        $item['target_path'] = $targetPath;
        $item['deployed'] = true;
      }
    } else {
      $item['target_path'] = rtrim($publicRoot, '/') . '/apps/' . trim($appFolder, '/') . '/' . (string) $resolved['relative'];
    }

    $commandPath = $item['target_path'] !== '' ? $item['target_path'] : (string) $resolved['path'];
    $item['command'] = plugin_cron_command($task, $commandPath);
    $item['cron_line'] = trim((string) $task['schedule']) . ' ' . $item['command'];
    $items[] = $item;
  }

  return [
    'ok' => empty($errors),
    'tasks' => $items,
    'errors' => $errors,
    'private_root' => $privateRoot,
  ];
}

function plugin_install(PDO $pdo, array $ctx = []): array
{
  global $PLUGIN_META;

  $publicRoot = $ctx['public_root'] ?? realpath(__DIR__ . '/../../..');
  $pluginRoot = $ctx['plugin_root'] ?? realpath(__DIR__ . '/..');
  $groupName = trim((string) ($ctx['group_name'] ?? ''));
  if ($groupName === '') {
    $groupName = (string) ($PLUGIN_META['default_group_name'] ?? 'Mis apps');
  }

  $pluginId = plugin_meta_plugin_id($PLUGIN_META, $ctx);
  $appFolder = plugin_meta_app_folder($PLUGIN_META, $ctx);
  $sourceUrl = (string) ($ctx['source_url'] ?? '');
  $sha256 = (string) ($ctx['sha256'] ?? '');
  $version = (string) ($ctx['version'] ?? ($PLUGIN_META['version'] ?? '1.0.0'));
  $sqlStrategy = (string) ($ctx['sql_strategy'] ?? 'auto');
  $adminDb = is_array($ctx['admin_db'] ?? null) ? $ctx['admin_db'] : [];

  if ($pluginId === '') {
    return [
      'ok' => false,
      'msg' => 'No se pudo instalar el plugin: falta el identificador del plugin.',
      'sql' => [
        'ok' => true,
        'executed' => [],
        'manual' => [],
        'errors' => [],
        'connection' => '',
      ],
    ];
  }

  $sqlResult = plugin_apply_sql_scripts($pdo, [
    'plugin_root' => $pluginRoot,
    'app_folder' => $appFolder,
    'sql_strategy' => $sqlStrategy,
    'admin_db' => $adminDb,
  ]);

  if ($sqlStrategy === 'show') {
    return [
      'ok' => true,
      'sql_only' => true,
      'msg' => 'SQL preparados para ejecucion manual.',
      'sql' => $sqlResult,
    ];
  }

  if (!$sqlResult['ok']) {
    return [
      'ok' => false,
      'msg' => 'No se pudieron ejecutar todos los SQL automaticos.',
      'sql' => $sqlResult,
    ];
  }

  if ($appFolder !== '') {
    $appsDir = $publicRoot . '/apps';
    $linkPath = $appsDir . '/' . $appFolder;
    $relativeTarget = '../plugins/' . $pluginId . '/apps/' . $appFolder;
    $absTarget = $pluginRoot . '/apps/' . $appFolder;

    if (is_dir($appsDir) && is_dir($absTarget)) {
      if (is_link($linkPath)) {
        $currentTarget = plugin_normalize_link_target((string) @readlink($linkPath));
        $expectedTargets = [
          plugin_normalize_link_target($relativeTarget),
          plugin_normalize_link_target($absTarget),
        ];
        if (!in_array($currentTarget, $expectedTargets, true)) {
          @unlink($linkPath);
        }
      }

      if (!is_link($linkPath) && !file_exists($linkPath)) {
        @symlink($relativeTarget, $linkPath) || @symlink($absTarget, $linkPath);
      }
    }
  }

  $cronResult = plugin_prepare_scheduled_tasks([
    'public_root' => $publicRoot,
    'plugin_root' => $pluginRoot,
    'plugin_id' => $pluginId,
    'app_folder' => $appFolder,
  ]);

  $rolesJson = json_encode(plugin_normalize_roles($ctx['roles_json'] ?? ($PLUGIN_META['roles_json'] ?? ['administradores'])), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);

  $st = $pdo->prepare("SELECT id FROM intranet_apps WHERE app_id=? LIMIT 1");
  $st->execute([$pluginId]);
  $row = $st->fetch(PDO::FETCH_ASSOC);

  if (!$row) {
    $pos = 0;
    try {
      $p = $pdo->prepare("SELECT COALESCE(MAX(position),0)+1 FROM intranet_apps WHERE group_name=?");
      $p->execute([$groupName]);
      $pos = (int) ($p->fetchColumn() ?: 0);
    } catch (Throwable $e) {
    }

    $ins = $pdo->prepare("
            INSERT INTO intranet_apps
            (app_id, name, url, group_name, description, icon, image, new_tab, roles_json, position, is_enabled, created_at, updated_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 1, NOW(), NOW())
        ");
    $ins->execute([
      $pluginId,
      (string) $PLUGIN_META['name'],
      (string) $PLUGIN_META['url'],
      $groupName,
      (string) ($PLUGIN_META['description'] ?? ''),
      (string) ($PLUGIN_META['icon'] ?? ''),
      $PLUGIN_META['image'] ?? null,
      (int) ($PLUGIN_META['new_tab'] ?? 0),
      $rolesJson,
      $pos
    ]);
  } else {
    $up = $pdo->prepare("
            UPDATE intranet_apps
            SET
                name=?,
                url=?,
                group_name=?,
                description=?,
                icon=?,
                image=?,
                new_tab=?,
                roles_json=?,
                is_enabled=1,
                updated_at=NOW()
            WHERE app_id=?
        ");
    $up->execute([
      (string) $PLUGIN_META['name'],
      (string) $PLUGIN_META['url'],
      $groupName,
      (string) ($PLUGIN_META['description'] ?? ''),
      (string) ($PLUGIN_META['icon'] ?? ''),
      $PLUGIN_META['image'] ?? null,
      (int) ($PLUGIN_META['new_tab'] ?? 0),
      $rolesJson,
      $pluginId
    ]);
  }

  $st2 = $pdo->prepare("SELECT id FROM intranet_plugins WHERE id=? LIMIT 1");
  $st2->execute([$pluginId]);
  $exists = (bool) $st2->fetch(PDO::FETCH_ASSOC);

  if ($exists) {
    $up2 = $pdo->prepare("
            UPDATE intranet_plugins
            SET
                name=?,
                version=?,
                enabled=1,
                source_url=?,
                sha256=?,
                updated_at=NOW()
            WHERE id=?
        ");
    $up2->execute([
      (string) $PLUGIN_META['name'],
      $version,
      $sourceUrl !== '' ? $sourceUrl : null,
      $sha256 !== '' ? $sha256 : null,
      $pluginId
    ]);
  } else {
    $ins2 = $pdo->prepare("
            INSERT INTO intranet_plugins
            (id, name, version, enabled, source_url, sha256, installed_at, updated_at)
            VALUES (?, ?, ?, 1, ?, ?, NOW(), NOW())
        ");
    $ins2->execute([
      $pluginId,
      (string) $PLUGIN_META['name'],
      $version,
      $sourceUrl !== '' ? $sourceUrl : null,
      $sha256 !== '' ? $sha256 : null
    ]);
  }

  return [
    'ok' => true,
    'msg' => !$cronResult['ok']
      ? 'Plugin instalado, pero revisa la configuracion de las tareas programadas.'
      : ($sqlStrategy === 'skip'
        ? 'Plugin instalado correctamente sin ejecutar SQL.'
        : 'Plugin instalado correctamente.'),
    'sql' => $sqlResult,
    'cron' => $cronResult,
  ];
}

if (isset($pdo_for_plugin) && $pdo_for_plugin instanceof PDO) {
  $ctx = $PLUGIN_INSTALL_CTX ?? [];
  $PLUGIN_INSTALL_RESULT = plugin_install($pdo_for_plugin, is_array($ctx) ? $ctx : []);
  return;
}

if (php_sapi_name() !== 'cli') {
  session_start();
  require_once dirname(__DIR__, 3) . '/common/db.php';
  require_once dirname(__DIR__, 3) . '/common/user_preferences.php';
  $rolesFile = dirname(__DIR__, 3) . '/common/roles.php';
  if (is_file($rolesFile)) {
    require_once $rolesFile;
  }
  $pdo = db();
  intranet_load_session_theme_mode($pdo);
  $result = null;

  $groups = ['Mis apps'];
  try {
    $rs = $pdo->query("SELECT DISTINCT group_name FROM intranet_apps WHERE group_name IS NOT NULL AND group_name<>'' ORDER BY group_name");
    $groups = [];
    while ($r = $rs->fetch(PDO::FETCH_ASSOC)) {
      $groups[] = $r['group_name'];
    }
    $groups = array_values(array_unique(array_filter($groups))) ?: ['Mis apps'];
  } catch (Throwable $e) {
  }

  $selectedRoles = plugin_normalize_roles($PLUGIN_META['roles_json'] ?? ['administradores']);
  $availableRoles = plugin_available_roles($pdo, $selectedRoles);

  if (($_SERVER['REQUEST_METHOD'] ?? 'GET') === 'POST') {
    $group = (string) ($_POST['group_name'] ?? '');
    $selectedRoles = plugin_normalize_roles($_POST['roles_json'] ?? $selectedRoles);
    $availableRoles = plugin_available_roles($pdo, $selectedRoles);
    $sqlStrategy = (string) ($_POST['sql_strategy'] ?? 'auto');
    $adminDb = [
      'host' => (string) ($_POST['db_admin_host'] ?? 'localhost'),
      'port' => (int) ($_POST['db_admin_port'] ?? 3306),
      'user' => (string) ($_POST['db_admin_user'] ?? ''),
      'pass' => (string) ($_POST['db_admin_pass'] ?? ''),
    ];
    $result = plugin_install($pdo, [
      'group_name' => $group,
      'roles_json' => $selectedRoles,
      'sql_strategy' => $sqlStrategy,
      'admin_db' => $adminDb,
    ]);
  }

  header('Content-Type: text/html; charset=utf-8');
  ?>
  <!doctype html>
  <html lang="es" class="app-html" <?= intranet_theme_html_attr(); ?>>

  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Instalar plugin</title>
    <?= intranet_theme_bootstrap_script_tag(); ?>
    <link rel="stylesheet" href="../../styles/css/style.css">
    <link rel="stylesheet" href="../../styles/css/theme-vars.css">
  </head>

  <body class="app-body">
    <div style="max-width:920px;margin:24px auto;display:grid;gap:16px;">
      <div style="background:var(--bg-card);border:1px solid var(--border-light);border-radius:12px;padding:18px;">
        <h2 style="margin:0 0 8px;">Instalar: <?php echo plugin_h((string) $PLUGIN_META['name']); ?></h2>
        <p style="margin:0;color:var(--text-muted);">Configura el grupo del dashboard y decide si quieres ejecutar los SQL con el usuario de aplicacion, con un usuario administrador de MySQL, no tocar tablas o solo mostrarlos para revision manual.</p>
      </div>

      <?php if (is_array($result)): ?>
        <div style="background:var(--bg-card);border:1px solid var(--border-light);border-radius:12px;padding:18px;">
          <h3 style="margin-top:0;"><?php echo !empty($result['ok']) ? 'Resultado' : 'Instalacion incompleta'; ?></h3>
          <p style="margin:0 0 12px;"><?php echo plugin_h((string) ($result['msg'] ?? '')); ?></p>

          <?php if (!empty($result['sql']['errors'])): ?>
            <div style="margin-bottom:14px; padding:12px; border:1px solid #fecaca; background:#fef2f2; border-radius:10px;">
              <strong>Errores SQL</strong>
              <ul style="margin:10px 0 0 18px;">
                <?php foreach ($result['sql']['errors'] as $error): ?>
                  <li><?php echo plugin_h((string) $error); ?></li>
                <?php endforeach; ?>
              </ul>
            </div>
          <?php endif; ?>

          <?php if (!empty($result['sql']['executed'])): ?>
            <div style="margin-bottom:14px;">
              <strong>SQL ejecutados</strong>
              <?php if (!empty($result['sql']['connection'])): ?>
                <div style="margin-top:4px;color:var(--text-muted);">Modo de ejecucion: <?php echo plugin_h((string) $result['sql']['connection']); ?></div>
              <?php endif; ?>
              <ul style="margin:10px 0 0 18px;">
                <?php foreach ($result['sql']['executed'] as $item): ?>
                  <li>
                    <?php echo plugin_h((string) $item['file']); ?>
                    (<?php echo plugin_h((string) $item['stage']); ?>, <?php echo (int) ($item['statement_count'] ?? 0); ?> sentencia(s), <?php echo plugin_h((string) ($item['connection'] ?? 'usuario de aplicacion')); ?>)
                  </li>
                <?php endforeach; ?>
              </ul>
            </div>
          <?php endif; ?>

          <?php if (!empty($result['sql']['manual'])): ?>
            <div style="display:grid; gap:12px;">
              <strong>SQL para revision o ejecucion manual</strong>
              <?php foreach ($result['sql']['manual'] as $item): ?>
                <div style="border:1px solid var(--border-light); border-radius:10px; padding:12px; background:var(--bg-input);">
                  <div style="margin-bottom:8px;">
                    <strong><?php echo plugin_h((string) $item['file']); ?></strong>
                    <span style="color:#667085;"> - <?php echo plugin_h((string) $item['stage']); ?> - <?php echo plugin_h((string) $item['mode']); ?></span>
                  </div>
                  <textarea readonly style="width:100%; min-height:220px; font-family:Consolas, monospace; font-size:12px; padding:10px;"><?php echo plugin_h((string) ($item['sql'] ?? '')); ?></textarea>
                </div>
              <?php endforeach; ?>
            </div>
          <?php endif; ?>

          <?php if (!empty($result['sql_only'])): ?>
            <p style="margin-top:14px; color:#8a5a00;">No se ha registrado el plugin en la intranet. Ejecuta los SQL necesarios para crear o actualizar tablas/vistas y luego vuelve a instalar en modo automatico.</p>
          <?php endif; ?>

          <?php if (!empty($result['cron']['tasks'])): ?>
            <div style="display:grid; gap:12px; margin-top:16px;">
              <strong>Tareas programadas</strong>

              <?php if (!empty($result['cron']['errors'])): ?>
                <div style="padding:12px; border:1px solid #fed7aa; background:#fff7ed; border-radius:10px;">
                  <strong>Revisar cron</strong>
                  <ul style="margin:10px 0 0 18px;">
                    <?php foreach ($result['cron']['errors'] as $error): ?>
                      <li><?php echo plugin_h((string) $error); ?></li>
                    <?php endforeach; ?>
                  </ul>
                </div>
              <?php endif; ?>

              <?php foreach ($result['cron']['tasks'] as $task): ?>
                <div style="border:1px solid var(--border-light); border-radius:10px; padding:12px; background:var(--bg-input); display:grid; gap:10px;">
                  <div>
                    <strong><?php echo plugin_h((string) ($task['name'] ?? 'Tarea')); ?></strong>
                    <span style="color:#667085;"> - <?php echo (($task['mode'] ?? 'manual') === 'managed') ? 'gestionada' : 'manual'; ?></span>
                  </div>

                  <?php if (!empty($task['description'])): ?>
                    <div style="color:var(--text-muted);"><?php echo plugin_h((string) $task['description']); ?></div>
                  <?php endif; ?>

                  <div><strong>Programacion:</strong> <code><?php echo plugin_h((string) ($task['schedule'] ?? '')); ?></code></div>
                  <div><strong>Script origen:</strong> <code><?php echo plugin_h((string) ($task['source_path'] ?? '')); ?></code></div>
                  <div><strong>Ruta a ejecutar:</strong> <code><?php echo plugin_h((string) (($task['target_path'] ?? '') ?: ($task['source_path'] ?? ''))); ?></code></div>

                  <?php if (!empty($task['error'])): ?>
                    <div style="color:#b42318;"><strong>Error:</strong> <?php echo plugin_h((string) $task['error']); ?></div>
                  <?php endif; ?>

                  <?php if (!empty($task['cron_line'])): ?>
                    <div>
                      <strong>Linea cron sugerida</strong>
                      <textarea readonly style="width:100%; min-height:84px; font-family:Consolas, monospace; font-size:12px; padding:10px;"><?php echo plugin_h((string) $task['cron_line']); ?></textarea>
                    </div>
                  <?php endif; ?>

                  <?php if (($task['mode'] ?? 'manual') === 'manual' && !empty($task['code'])): ?>
                    <div>
                      <strong>Codigo del script para copiar manualmente</strong>
                      <textarea readonly style="width:100%; min-height:220px; font-family:Consolas, monospace; font-size:12px; padding:10px;"><?php echo plugin_h((string) $task['code']); ?></textarea>
                    </div>
                  <?php endif; ?>
                </div>
              <?php endforeach; ?>
            </div>
          <?php endif; ?>
        </div>
      <?php endif; ?>

      <div style="background:var(--bg-card);border:1px solid var(--border-light);border-radius:12px;padding:18px;">
        <form method="post" style="display:grid; gap:14px;">
          <div>
            <label style="display:block;margin:0 0 6px;color:var(--text-main);font-weight:600;">Grupo</label>
            <select name="group_name" class="form-control" style="width:100%;padding:10px 12px;">
              <?php foreach ($groups as $g): ?>
                <option value="<?php echo plugin_h($g); ?>" <?php echo ($g === ($PLUGIN_META['default_group_name'] ?? '')) ? 'selected' : ''; ?>>
                  <?php echo plugin_h($g); ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div>
            <label style="display:block;margin:0 0 6px;color:var(--text-main);font-weight:600;">Roles</label>
            <select name="roles_json[]" class="form-control" multiple required style="width:100%;min-height:116px;padding:10px 12px;">
              <?php foreach ($availableRoles as $role): ?>
                <option value="<?php echo plugin_h($role); ?>" <?php echo in_array($role, $selectedRoles, true) ? 'selected' : ''; ?>>
                  <?php echo plugin_h($role); ?>
                </option>
              <?php endforeach; ?>
            </select>
          </div>

          <div>
            <label style="display:block;margin:0 0 6px;color:var(--text-main);font-weight:600;">Estrategia SQL</label>
            <select name="sql_strategy" class="form-control" style="width:100%;padding:10px 12px;">
              <option value="auto">Ejecutar los SQL marcados como "Ejecutar" y mostrar los "Mostrar"</option>
              <option value="admin">Ejecutar los SQL con usuario administrador de MySQL</option>
              <option value="skip">Instalar sin tocar tablas ni ejecutar SQL</option>
              <option value="show">Solo mostrar todos los SQL para ejecucion manual del administrador</option>
            </select>
          </div>


          <div style="border:1px solid #e6e8ee; border-radius:10px; padding:12px; background:#fafafa;">
            <strong>Credenciales MySQL administrador</strong>
            <p style="margin:8px 0 10px;color:var(--text-muted);">Solo se usan si eliges el modo administrador.</p>
            <div style="display:grid; gap:10px; grid-template-columns:repeat(auto-fit,minmax(180px,1fr));">
              <div>
                <label style="display:block;margin:0 0 6px;">Host</label>
                <input type="text" name="db_admin_host" value="localhost" class="form-control" style="width:100%;padding:10px 12px;">
              </div>
              <div>
                <label style="display:block;margin:0 0 6px;">Puerto</label>
                <input type="number" name="db_admin_port" value="3306" class="form-control" style="width:100%;padding:10px 12px;">
              </div>
              <div>
                <label style="display:block;margin:0 0 6px;">Usuario admin</label>
                <input type="text" name="db_admin_user" class="form-control" style="width:100%;padding:10px 12px;" placeholder="root">
              </div>
              <div>
                <label style="display:block;margin:0 0 6px;">Password admin</label>
                <input type="password" name="db_admin_pass" class="form-control" style="width:100%;padding:10px 12px;">
              </div>
            </div>
          </div>
          <?php $sqlScripts = plugin_sorted_sql_scripts($PLUGIN_META['sql_scripts'] ?? []); ?>
          <?php if (!empty($sqlScripts)): ?>
            <div style="border:1px solid #e6e8ee; border-radius:10px; padding:12px; background:#fafafa;">
              <strong>Scripts incluidos en este plugin</strong>
              <ul style="margin:10px 0 0 18px;">
                <?php foreach ($sqlScripts as $item): ?>
                  <li>
                    <?php echo plugin_h((string) $item['file']); ?>
                    - orden <?php echo (int) ($item['order'] ?? 0); ?>
                    - <?php echo plugin_h((string) ($item['stage'] ?? 'update')); ?>
                    - <?php echo (($item['mode'] ?? 'auto') === 'manual') ? 'mostrar' : 'ejecutar'; ?>
                  </li>
                <?php endforeach; ?>
              </ul>
            </div>
          <?php endif; ?>

          <?php $scheduledTasks = plugin_scheduled_tasks($PLUGIN_META['scheduled_tasks'] ?? []); ?>
          <?php if (!empty($scheduledTasks)): ?>
            <div style="border:1px solid #e6e8ee; border-radius:10px; padding:12px; background:#fafafa; display:grid; gap:10px;">
              <strong>Tareas programadas incluidas</strong>
              <div style="color:var(--text-muted);">
                Las tareas en modo manual mostraran el comando y el codigo. Las tareas gestionadas se desplegaran en <code>/var/cron/&lt;plugin&gt;/</code> con un <code>.htaccess</code> de bloqueo para acceso web.
              </div>
              <ul style="margin:0 0 0 18px;">
                <?php foreach ($scheduledTasks as $task): ?>
                  <li>
                    <?php echo plugin_h((string) $task['name']); ?>
                    - <code><?php echo plugin_h((string) $task['schedule']); ?></code>
                    - <?php echo (($task['mode'] ?? 'manual') === 'managed') ? 'gestionada' : 'manual'; ?>
                    - <code><?php echo plugin_h((string) $task['script']); ?></code>
                  </li>
                <?php endforeach; ?>
              </ul>
            </div>
          <?php endif; ?>

          <?php $supportScripts = plugin_support_scripts($PLUGIN_META['support_scripts'] ?? []); ?>
          <?php if (!empty($supportScripts)): ?>
            <div style="border:1px solid #e6e8ee; border-radius:10px; padding:12px; background:#fafafa; display:grid; gap:10px;">
              <strong>Scripts auxiliares incluidos</strong>
              <div style="color:var(--text-muted);">
                Estos scripts se empaquetan con el plugin para ejecucion manual o despliegues administrativos. El instalador no los ejecuta automaticamente.
              </div>
              <ul style="margin:0 0 0 18px;">
                <?php foreach ($supportScripts as $script): ?>
                  <li style="margin-bottom:10px;">
                    <code><?php echo plugin_h((string) $script['package_path']); ?></code>
                    <?php if (!empty($script['description'])): ?>
                      <div><?php echo plugin_h((string) $script['description']); ?></div>
                    <?php endif; ?>
                    <?php if (!empty($script['instructions'])): ?>
                      <div><strong>Como ejecutarlo:</strong> <?php echo plugin_h((string) $script['instructions']); ?></div>
                    <?php endif; ?>
                  </li>
                <?php endforeach; ?>
              </ul>
            </div>
          <?php endif; ?>

          <div><button class="btn-mini primary" type="submit">Continuar</button></div>
        </form>
      </div>
    </div>
  </body>

  </html>
<?php
}
