<?php
declare(strict_types=1);

require_once dirname(__DIR__, 2) . '/common/app_guard.php';
require_once dirname(__DIR__, 2) . '/common/url.php';
checkAppAccess();

require_once dirname(__DIR__, 2) . '/common/catalogo_db.php';
require_once dirname(__DIR__, 2) . '/common/plugin_store.php';

if (!catalogo_is_admin()) {
    renderErrorPage('No tienes permisos para acceder a <em>Actualizaciones del núcleo</em>.');
    exit;
}

$pdo = db();
core_update_ensure_app_registered($pdo);

function updates_parse_bool_value($value): ?bool
{
    if (is_bool($value)) {
        return $value;
    }
    if (is_int($value) || is_float($value)) {
        return (int) $value === 1;
    }

    $normalized = strtolower(trim((string) $value));
    if ($normalized === '') {
        return null;
    }
    if (in_array($normalized, ['1', 'true', 'yes', 'on'], true)) {
        return true;
    }
    if (in_array($normalized, ['0', 'false', 'no', 'off'], true)) {
        return false;
    }

    return null;
}

function updates_read_define_from_files(string $constName, array $files): ?string
{
    foreach ($files as $file) {
        if (!is_file($file) || !is_readable($file)) {
            continue;
        }

        $raw = @file_get_contents($file);
        if (!is_string($raw) || $raw === '') {
            continue;
        }

        if (preg_match('/define\s*\(\s*[\'\"]' . preg_quote($constName, '/') . '[\'\"]\s*,\s*(.*?)\s*\)\s*;/is', $raw, $m)) {
            return trim((string) $m[1], " \t\n\r\0\x0B'\"");
        }
    }

    return null;
}

function updates_explicit_core_build_enabled(): ?bool
{
    $candidates = [
        '/etc/intranet/intranet-config.php',
        '/etc/csif/intranet-config.php',
        dirname(__DIR__, 2) . '/common/config.php',
        dirname(__DIR__, 2) . '/common/config.txt',
    ];

    $value = updates_read_define_from_files('CORE_UPDATE_BUILD_ENABLED', $candidates);
    if ($value !== null) {
        return updates_parse_bool_value($value);
    }

    if (defined('CORE_UPDATE_BUILD_ENABLED')) {
        return updates_parse_bool_value(CORE_UPDATE_BUILD_ENABLED);
    }

    return null;
}

function updates_is_dev_like_installation(): bool
{
    $candidates = [
        $_SERVER['HTTP_HOST'] ?? '',
        $_SERVER['SERVER_NAME'] ?? '',
        php_uname('n'),
        gethostname() ?: '',
    ];

    foreach ($candidates as $candidate) {
        $value = strtolower(trim((string) $candidate));
        if ($value === '') {
            continue;
        }

        if (
            $value === 'localhost'
            || $value === '127.0.0.1'
            || strpos($value, '.local') !== false
            || strpos($value, 'dev') !== false
            || strpos($value, 'desarrollo') !== false
            || strpos($value, 'pre') !== false
            || strpos($value, 'preprodu') !== false
            || strpos($value, 'staging') !== false
            || strpos($value, 'test') !== false
        ) {
            return true;
        }
    }

    return false;
}

function updates_core_build_enabled(string $currentVersion = '', string $latestRemote = ''): bool
{
    $explicit = updates_explicit_core_build_enabled();
    if ($explicit !== null) {
        return $explicit;
    }

    if (updates_is_dev_like_installation()) {
        return true;
    }

    if ($currentVersion !== '') {
        if ($latestRemote === '') {
            return true;
        }
        return version_compare($currentVersion, $latestRemote, '>=');
    }

    return false;
}

function updates_core_build_reason(string $currentVersion = '', string $latestRemote = ''): string
{
    $explicit = updates_explicit_core_build_enabled();
    if ($explicit === true) {
        return 'Habilitada por configuración explícita en /etc o por fallback local.';
    }
    if ($explicit === false) {
        return 'Deshabilitada por configuración explícita en /etc o por fallback local.';
    }
    if (updates_is_dev_like_installation()) {
        return 'Habilitada automáticamente al detectar una instalación de desarrollo o preproducción.';
    }
    if ($currentVersion !== '' && $latestRemote !== '' && version_compare($currentVersion, $latestRemote, '>=')) {
        return 'Habilitada automáticamente porque esta instalación ya está en la versión más reciente conocida.';
    }
    if ($currentVersion !== '' && $latestRemote === '') {
        return 'Habilitada automáticamente porque no hay una versión remota más reciente con la que comparar.';
    }

    return 'Deshabilitada.';
}

function updates_build_diagnostics(string $currentVersion = '', string $latestRemote = ''): array
{
    $explicit = updates_explicit_core_build_enabled();
    $host = (string) ($_SERVER['HTTP_HOST'] ?? $_SERVER['SERVER_NAME'] ?? gethostname() ?: '');
    $devLike = updates_is_dev_like_installation();

    return [
        'current_version' => $currentVersion,
        'latest_remote' => $latestRemote,
        'explicit_config' => $explicit === null ? 'sin definir' : ($explicit ? 'true' : 'false'),
        'dev_like' => $devLike ? 'si' : 'no',
        'http_host' => $host !== '' ? $host : '(vacío)',
        'publish_url' => updates_publish_url() !== '' ? updates_publish_url() : '(sin configurar)',
        'build_enabled' => updates_core_build_enabled($currentVersion, $latestRemote) ? 'si' : 'no',
        'reason' => updates_core_build_reason($currentVersion, $latestRemote),
    ];
}

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

function updates_redirect(string $status): void
{
    $target = asset_url('dashboard.php') . '?open=' . rawurlencode('base/updates/index.php?oku=' . $status)
        . '&full_reload=1&ts=' . time();

    if (!headers_sent()) {
        header('Content-Type: text/html; charset=utf-8');
    }

    echo '<!doctype html><html lang="es"><head><meta charset="utf-8"><title>Redirigiendo...</title></head><body>';
    echo '<script>';
    echo 'window.top.location.replace(' . json_encode($target, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) . ');';
    echo '</script>';
    echo '<noscript><meta http-equiv="refresh" content="0;url=' . h($target) . '"></noscript>';
    echo '</body></html>';
    exit;
}

function updates_notice_map(): array
{
    return [
        'uploaded' => 'Paquete de actualización subido correctamente.',
        'installed' => 'Actualización del núcleo aplicada correctamente.',
        'deleted' => 'Paquete local eliminado.',
        'generated' => 'Paquete core-update generado correctamente.',
        'published' => 'Paquete generado y publicado correctamente en el Store.',
    ];
}

function updates_human_size(int $bytes): string
{
    if ($bytes < 1024) return $bytes . ' B';
    if ($bytes < 1048576) return number_format($bytes / 1024, 1, ',', '.') . ' KB';
    return number_format($bytes / 1048576, 2, ',', '.') . ' MB';
}

function updates_increment_version(string $version): string
{
    $version = trim($version);
    if ($version === '') {
        return date('Y.m.d') . '.1';
    }

    if (preg_match('/^(.*?)(\d+)$/', $version, $m)) {
        $prefix = (string) $m[1];
        $number = (int) $m[2] + 1;
        return $prefix . $number;
    }

    return $version . '.1';
}

function updates_version_revision_for_date(string $version, string $datePrefix): ?int
{
    $pattern = '/^' . preg_quote($datePrefix, '/') . '\.(\d+)$/';
    if (!preg_match($pattern, trim($version), $m)) {
        return null;
    }

    return (int) $m[1];
}

function updates_next_build_version(): string
{
    $todayPrefix = date('Y.m.d');
    $errors = [];
    $builds = core_update_list_builds($errors);
    $current = core_update_current_version();
    $latestBuild = core_update_latest_version($builds);
    $maxRevision = 0;

    $currentRevision = updates_version_revision_for_date($current, $todayPrefix);
    if ($currentRevision !== null) {
        $maxRevision = max($maxRevision, $currentRevision);
    }

    $buildRevision = updates_version_revision_for_date($latestBuild, $todayPrefix);
    if ($buildRevision !== null) {
        $maxRevision = max($maxRevision, $buildRevision);
    }

    return $todayPrefix . '.' . ($maxRevision + 1);
}

function updates_read_log(): array
{
    $path = core_updates_base_dir() . '/install-log.json';
    if (!is_file($path)) {
        return [];
    }
    $data = json_decode((string) file_get_contents($path), true);
    return is_array($data) ? array_reverse($data) : [];
}


function updates_publish_url(): string
{
    $normalize = static function (string $url): string {
        $url = trim($url);
        if ($url === '') {
            return '';
        }

        $legacySuffix = '/public/api/publish-core-update.php';
        if (str_ends_with($url, $legacySuffix)) {
            return substr($url, 0, -strlen($legacySuffix)) . '/api/publish-core-update.php';
        }

        return $url;
    };

    foreach (['/etc/intranet/intranet-config.php', '/etc/csif/intranet-config.php'] as $cfg) {
        if (!is_file($cfg)) {
            continue;
        }
        require_once $cfg;
        if (defined('PLUGIN_STORE_PUBLISH_URL') && PLUGIN_STORE_PUBLISH_URL) {
            return $normalize((string) PLUGIN_STORE_PUBLISH_URL);
        }
        if (defined('PLUGIN_STORE_BASE_URL') && PLUGIN_STORE_BASE_URL) {
            return $normalize(rtrim((string) PLUGIN_STORE_BASE_URL, '/') . '/api/publish-core-update.php');
        }
        break;
    }
    if (defined('PLUGIN_STORE_PUBLISH_URL') && PLUGIN_STORE_PUBLISH_URL) {
        return $normalize((string) PLUGIN_STORE_PUBLISH_URL);
    }
    if (defined('PLUGIN_STORE_BASE_URL') && PLUGIN_STORE_BASE_URL) {
        return $normalize(rtrim((string) PLUGIN_STORE_BASE_URL, '/') . '/api/publish-core-update.php');
    }
    return '';
}

function updates_publish_token(): string
{
    foreach (['/etc/intranet/intranet-config.php', '/etc/csif/intranet-config.php'] as $cfg) {
        if (!is_file($cfg)) {
            continue;
        }
        require_once $cfg;
        if (defined('PLUGIN_STORE_PUBLISH_TOKEN') && PLUGIN_STORE_PUBLISH_TOKEN) {
            return (string) PLUGIN_STORE_PUBLISH_TOKEN;
        }
        break;
    }
    if (defined('PLUGIN_STORE_PUBLISH_TOKEN') && PLUGIN_STORE_PUBLISH_TOKEN) {
        return (string) PLUGIN_STORE_PUBLISH_TOKEN;
    }
    return '';
}

function updates_publish_enabled(): bool
{
    return updates_publish_url() !== '' && updates_publish_token() !== '';
}

function updates_response_preview(string $raw, int $limit = 240): string
{
    $raw = trim($raw);
    if ($raw === '') {
        return '(respuesta vacia)';
    }

    $preview = strip_tags($raw);
    if ($preview === '') {
        $preview = $raw;
    }

    $preview = preg_replace('/\s+/u', ' ', $preview) ?: $preview;
    if (function_exists('mb_strlen') && function_exists('mb_substr')) {
        if (mb_strlen($preview, 'UTF-8') > $limit) {
            return mb_substr($preview, 0, $limit, 'UTF-8') . '...';
        }
        return $preview;
    }

    if (strlen($preview) > $limit) {
        return substr($preview, 0, $limit) . '...';
    }

    return $preview;
}

function updates_publish_to_store(string $zipPath, string $version, string $description, array &$errors): bool
{
    $url = updates_publish_url();
    $token = updates_publish_token();
    if ($url === '' || $token === '') {
        $errors[] = 'Falta configurar PLUGIN_STORE_PUBLISH_URL o PLUGIN_STORE_PUBLISH_TOKEN.';
        return false;
    }
    if (!is_file($zipPath)) {
        $errors[] = 'No existe el ZIP a publicar en el Store.';
        return false;
    }
    if (!function_exists('curl_init')) {
        $errors[] = 'La extensión cURL no está disponible en PHP.';
        return false;
    }

    $ch = curl_init($url);
    $post = [
        'file' => new CURLFile($zipPath, 'application/zip', basename($zipPath)),
        'force' => '0',
    ];
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_POSTFIELDS => $post,
        CURLOPT_HTTPHEADER => [
            'X-Plugin-Token: ' . $token,
            'X-Core-Version: ' . $version,
            'X-Core-Description: ' . $description,
            'Expect:',
        ],
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 120,
        CURLOPT_SSL_VERIFYPEER => true,
        CURLOPT_SSL_VERIFYHOST => 2,
        CURLOPT_SAFE_UPLOAD => true,
    ]);
    $resp = curl_exec($ch);
    $err = curl_error($ch);
    $code = (int) curl_getinfo($ch, CURLINFO_HTTP_CODE);
    $contentType = (string) curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
    curl_close($ch);

    if ($resp === false) {
        $errors[] = 'Error al publicar en el Store: ' . $err;
        return false;
    }
    $json = json_decode((string) $resp, true);
    if (!is_array($json)) {
        $errors[] = 'Respuesta no valida del Store al publicar el core-update (HTTP ' . $code . ', ' . ($contentType !== '' ? $contentType : 'sin Content-Type') . ').';
        $errors[] = 'Vista previa de respuesta: ' . updates_response_preview((string) $resp);
        return false;
    }
    if ($code !== 200 || empty($json['ok'])) {
        $msg = (string) ($json['message'] ?? $json['error'] ?? ('HTTP ' . $code));
        $errors[] = 'El Store rechazó la publicación: ' . $msg;
        return false;
    }
    return true;
}

$errors = [];
$notices = [];
$csrf = catalogo_csrf_token('core_updates_csrf');

$noticeMap = updates_notice_map();
if (isset($_GET['oku']) && isset($noticeMap[(string) $_GET['oku']])) {
    $notices[] = $noticeMap[(string) $_GET['oku']];
}

$currentVersion = core_update_current_version();
$remoteUpdates = core_update_fetch_index($errors);
$latestRemote = core_update_latest_version($remoteUpdates);
$buildEnabled = updates_core_build_enabled($currentVersion, $latestRemote);
$buildEnabledReason = updates_core_build_reason($currentVersion, $latestRemote);
$buildDiagnostics = updates_build_diagnostics($currentVersion, $latestRemote);

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    try {
        catalogo_csrf_validate($_POST['csrf'] ?? null, 'core_updates_csrf');
        $action = (string) ($_POST['action'] ?? '');

        if ($action === 'upload') {
            if (empty($_FILES['core_zip'])) {
                throw new RuntimeException('No se ha recibido el ZIP de actualización.');
            }
            $saved = core_update_save_uploaded_zip($_FILES['core_zip'], $errors);
            if (!$saved) {
                throw new RuntimeException('No se pudo guardar el paquete.');
            }
            $tmpErrors = [];
            $tmp = core_update_extract_to_tmp($saved, $tmpErrors);
            if (!$tmp) {
                @unlink($saved);
                throw new RuntimeException('El ZIP subido no es válido como actualización del núcleo.');
            }
            $manifest = core_update_read_manifest_from_dir($tmp, $tmpErrors);
            plugin_rrmdir($tmp);
            if (!$manifest) {
                @unlink($saved);
                throw new RuntimeException('El paquete subido no contiene un core-update válido.');
            }
            updates_redirect('uploaded');
        }

        if ($action === 'generate') {
            if (!$buildEnabled) {
                throw new RuntimeException('La generación de paquetes core-update está deshabilitada en esta instalación.');
            }
            $meta = [
                'version' => (string) ($_POST['build_version'] ?? ''),
                'description' => (string) ($_POST['build_description'] ?? ''),
                'include_docs' => isset($_POST['include_docs']),
                'include_guides' => isset($_POST['include_guides']),
                'include_sql' => isset($_POST['include_sql']),
                'sql_content' => (string) ($_POST['sql_content'] ?? ''),
            ];
            $res = core_update_generate_package($meta, (string) ($_SESSION['username'] ?? 'admin'), $errors);
            if (!$res) {
                throw new RuntimeException('No se pudo generar el paquete core-update.');
            }
            updates_redirect('generated');
        }


        if ($action === 'generate_and_publish') {
            if (!$buildEnabled) {
                throw new RuntimeException('La generación de paquetes core-update está deshabilitada en esta instalación.');
            }
            if (!updates_publish_enabled()) {
                throw new RuntimeException('Falta configurar la publicación al Store en esta instalación.');
            }
            $autoVersion = updates_next_build_version();
            $meta = [
                'version' => $autoVersion,
                'description' => (string) ($_POST['build_description'] ?? ''),
                'include_docs' => isset($_POST['include_docs']),
                'include_guides' => isset($_POST['include_guides']),
                'include_sql' => isset($_POST['include_sql']),
                'sql_content' => (string) ($_POST['sql_content'] ?? ''),
            ];
            $res = core_update_generate_package($meta, (string) ($_SESSION['username'] ?? 'admin'), $errors);
            if (!$res) {
                throw new RuntimeException('No se pudo generar el paquete core-update.');
            }
            $zipName = 'intranet-core-update-' . $autoVersion . '.zip';
            $zipPath = core_updates_base_dir() . '/builds/' . $zipName;
            if (!is_file($zipPath)) {
                throw new RuntimeException('Se generó la build pero no se encontró el ZIP resultante.');
            }
            if (!updates_publish_to_store($zipPath, $autoVersion, (string) $meta['description'], $errors)) {
                throw new RuntimeException('El paquete se generó localmente, pero no se pudo publicar en el Store.');
            }
            updates_redirect('published');
        }

        if ($action === 'delete_local') {
            $file = basename((string) ($_POST['package_file'] ?? ''));
            if ($file === '') {
                throw new RuntimeException('Paquete no válido.');
            }
            $path = core_updates_base_dir() . '/packages/' . $file;
            if (is_file($path)) {
                @unlink($path);
            }
            updates_redirect('deleted');
        }

        $options = [
            'preserve_theme' => isset($_POST['preserve_theme']),
            'preserve_images' => isset($_POST['preserve_images']),
            'preserve_apps' => true,
            'preserve_plugins' => true,
            'apply_sql' => isset($_POST['apply_sql']),
        ];

        if ($action === 'install_local') {
            $file = basename((string) ($_POST['package_file'] ?? ''));
            if ($file === '') {
                throw new RuntimeException('Paquete no válido.');
            }
            $path = core_updates_base_dir() . '/packages/' . $file;
            if (!is_file($path)) {
                throw new RuntimeException('No existe el paquete local seleccionado.');
            }
            $res = core_update_apply_from_zip($pdo, $path, (string) ($_SESSION['username'] ?? 'admin'), $errors, $options);
            if (!$res) {
                throw new RuntimeException('No se pudo aplicar la actualización del núcleo.');
            }
            updates_redirect('installed');
        }

        if ($action === 'install_remote') {
            $id = trim((string) ($_POST['core_id'] ?? ''));
            if ($id === '') {
                throw new RuntimeException('Actualización no válida.');
            }
            $remote = core_update_fetch_index($errors);
            $meta = null;
            foreach ($remote as $item) {
                if ((string) ($item['id'] ?? '') === $id) {
                    $meta = $item;
                    break;
                }
            }
            if (!$meta) {
                throw new RuntimeException('La actualización no existe en el store.');
            }
            $zipUrl = (string) ($meta['zip'] ?? '');
            if ($zipUrl === '') {
                throw new RuntimeException('La actualización no define URL de ZIP.');
            }
            $zipUrl = plugin_store_join_url(plugin_store_base_url(), $zipUrl);
            $sha256 = (string) ($meta['sha256'] ?? '');
            $tmpZip = plugin_download_to_tmp($zipUrl, $sha256, $errors);
            if (!$tmpZip) {
                throw new RuntimeException('No se pudo descargar el ZIP de actualización.');
            }
            $res = core_update_apply_from_zip($pdo, $tmpZip, (string) ($_SESSION['username'] ?? 'admin'), $errors, $options);
            @unlink($tmpZip);
            if (!$res) {
                throw new RuntimeException('No se pudo aplicar la actualización del núcleo.');
            }
            updates_redirect('installed');
        }
    } catch (Throwable $e) {
        $errors[] = $e->getMessage();
    }
}

$localUpdates = core_update_list_local_packages($errors);
$builds = core_update_list_builds($errors);
$latestLocal = core_update_latest_version($localUpdates);
$latestBuild = core_update_latest_version($builds);
$latestAvailable = $latestRemote !== '' ? $latestRemote : ($latestBuild !== '' ? $latestBuild : $latestLocal);
$log = updates_read_log();
$storeBase = plugin_store_base_url();
$publishEnabled = updates_publish_enabled();
?>
<!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>Actualizaciones del núcleo</title>
  <?= intranet_theme_bootstrap_script_tag(); ?>
  <link rel="stylesheet" href="<?= h(asset_url('styles/css/style.css')) ?>">
  <link rel="stylesheet" href="../../styles/css/theme-vars.css?v=<?= urlencode((string) (@filemtime(__DIR__ . '/../../styles/css/theme-vars.css') ?: time())) ?>">
  <style>
    .updates-page{display:grid;gap:0}
    .updates-page .card{
      background:color-mix(in srgb,var(--bg-card) 90%,var(--bg-input) 10%);
      border-radius:16px;
      box-shadow:0 12px 28px rgba(15,23,42,.08);
    }
    .updates-page .card-title{margin-bottom:18px}
    .updates-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px;margin-bottom:20px}
    .updates-stat{
      padding:18px;
      border:1px solid var(--border-light);
      border-radius:16px;
      background:color-mix(in srgb,var(--bg-card) 78%,var(--bg-input) 22%);
      box-shadow:inset 0 1px 0 rgba(255,255,255,.18);
    }
    .updates-stat h3{margin:0 0 8px;font-size:1rem}
    .updates-stat .value{font-size:1.5rem;font-weight:800;color:var(--primary)}
    .updates-meta{color:var(--text-muted);font-size:.92rem}
    .updates-page .btn.primary,
    .updates-page .btn.danger{
      display:inline-flex;
      align-items:center;
      justify-content:center;
      gap:8px;
      padding:10px 16px;
      border:none;
      border-radius:10px;
      font-weight:800;
      cursor:pointer;
      text-decoration:none;
      transition:transform .2s ease,filter .2s ease,box-shadow .2s ease;
    }
    .updates-page .btn.primary{
      background:linear-gradient(135deg,var(--primary),var(--accent));
      color:var(--text-inverse);
      box-shadow:0 8px 20px rgba(6,123,59,.16);
    }
    .updates-page .btn.danger{
      background:var(--danger-btn);
      color:#fff;
      box-shadow:0 8px 20px rgba(185,28,28,.14);
    }
    .updates-page .btn.primary:hover,
    .updates-page .btn.danger:hover{
      transform:translateY(-1px);
      filter:brightness(.98);
    }
    .updates-card-title{display:flex;justify-content:space-between;gap:12px;align-items:center;flex-wrap:wrap}
    .updates-badge{display:inline-flex;align-items:center;padding:4px 10px;border-radius:999px;background:var(--primary-light);color:var(--primary-dark);font-weight:700;font-size:.85rem;border:1px solid color-mix(in srgb,var(--primary) 16%,transparent)}
    .updates-form-grid{display:grid;gap:14px}
    .updates-form-grid .form-group{margin-bottom:0}
    .updates-form-grid label{display:block;margin-bottom:6px;font-weight:700;color:var(--text-main)}
    .updates-form-grid .form-control{display:block;width:100%;min-height:44px}
    .updates-page .form-control{
      background:color-mix(in srgb,var(--bg-input) 84%,var(--bg-card) 16%);
    }
    .updates-page code{
      padding:2px 6px;
      border-radius:8px;
      background:color-mix(in srgb,var(--bg-input) 74%,var(--bg-card) 26%);
      color:var(--text-main);
    }
    .updates-alert{margin-bottom:12px}
    .updates-table-wrap{
      border:1px solid var(--border-light);
      border-radius:12px;
      overflow:hidden;
      background:color-mix(in srgb,var(--bg-card) 88%,var(--bg-input) 12%);
    }
    .updates-table-wrap table{background:transparent}
    .updates-table-wrap th{background:color-mix(in srgb,var(--bg-input) 72%,var(--bg-card) 28%)}
    .updates-table-wrap td,.updates-table-wrap th{border-color:var(--border-light)}
    .updates-diagnostic th{
      width:280px;
      background:color-mix(in srgb,var(--bg-input) 62%,var(--bg-card) 38%);
    }
    .updates-intro-note{margin-top:0;margin-bottom:14px}
    .updates-sql-group{margin-top:14px}
    .updates-section-heading{margin:0}
    .updates-inline-form{margin:0}
    .updates-inline-form + .updates-inline-form{margin-top:8px}
    html[data-theme="dark"] .updates-page .card{
      background:color-mix(in srgb,var(--bg-card) 94%,black 6%);
      box-shadow:0 16px 34px rgba(0,0,0,.22);
    }
    html[data-theme="dark"] .updates-stat,
    html[data-theme="dark"] .updates-table-wrap{
      background:color-mix(in srgb,var(--bg-input) 84%,var(--bg-card) 16%);
      box-shadow:none;
    }
    html[data-theme="dark"] .updates-page .form-control,
    html[data-theme="dark"] .updates-page code{
      background:color-mix(in srgb,var(--bg-input) 82%,black 18%);
    }
    html[data-theme="dark"] .updates-table-wrap th,
    html[data-theme="dark"] .updates-diagnostic th{
      background:color-mix(in srgb,var(--bg-input) 68%,black 32%);
    }
    html[data-theme="dark"] .updates-badge{
      background:rgba(255,255,255,.08);
      color:var(--text-main);
      border-color:rgba(255,255,255,.14);
    }
    @media (max-width: 960px){.updates-grid{grid-template-columns:1fr}}
  </style>
</head>
<body class="app-body">
<div class="app-container updates-page">
  <header class="header-top">
    <div>
      <h2 style="margin:0;">Actualizaciones del núcleo</h2>
      <p style="margin:5px 0 0 0; opacity:0.9;">Versión instalada, versiones disponibles y asistente de actualización segura.</p>
      <p style="margin:6px 0 0 0; font-size:.9rem; opacity:.85;">Store: <code><?= h($storeBase) ?></code></p>
    </div>
    <div class="header-actions">
      <a href="<?= h(asset_url('dashboard.php')) ?>" target="_top" class="btn-back-dashboard">Volver al inicio</a>
    </div>
  </header>

  <?php foreach ($errors as $e): ?>
    <div class="alert alert-error updates-alert"><?= h($e) ?></div>
  <?php endforeach; ?>
  <?php foreach ($notices as $n): ?>
    <div class="alert admin-notice updates-alert"><?= h($n) ?></div>
  <?php endforeach; ?>

  <section class="card">
    <div class="card-title">Estado de la intranet</div>
    <div class="updates-grid">
      <div class="updates-stat">
        <h3>Versión ejecutándose</h3>
        <div class="value"><?= h($currentVersion) ?></div>
        <div class="updates-meta">Versión actual del núcleo en esta instalación.</div>
      </div>
      <div class="updates-stat">
        <h3>Versión más reciente local</h3>
        <div class="value"><?= h($latestLocal !== '' ? $latestLocal : '-') ?></div>
        <div class="updates-meta">Paquetes ZIP subidos manualmente a esta sede.</div>
      </div>
      <div class="updates-stat">
        <h3>Último paquete generado</h3>
        <div class="value"><?= h($latestBuild !== '' ? $latestBuild : '-') ?></div>
        <div class="updates-meta">Builds core-update creadas automáticamente desde esta instalación.</div>
      </div>
      <div class="updates-stat">
        <h3>Versión más reciente publicada</h3>
        <div class="value"><?= h($latestRemote !== '' ? $latestRemote : '-') ?></div>
        <div class="updates-meta">Disponible desde el Store remoto.</div>
      </div>
    </div>
    <div class="updates-meta">Última versión disponible detectada: <strong><?= h($latestAvailable !== '' ? $latestAvailable : '-') ?></strong>.</div>
  </section>

  <section class="card">
    <div class="card-title">Diagnóstico de generación core-update</div>
    <div class="table-responsive updates-table-wrap updates-diagnostic">
      <table>
        <tbody>
          <tr>
            <th style="width:280px;">Generación habilitada</th>
            <td><strong><?= h((string) ($buildDiagnostics['build_enabled'] ?? 'no')) ?></strong></td>
          </tr>
          <tr>
            <th>Motivo</th>
            <td><?= h((string) ($buildDiagnostics['reason'] ?? '-')) ?></td>
          </tr>
          <tr>
            <th>Configuración explícita</th>
            <td><?= h((string) ($buildDiagnostics['explicit_config'] ?? 'sin definir')) ?></td>
          </tr>
          <tr>
            <th>Instalación tipo desarrollo/preproducción</th>
            <td><?= h((string) ($buildDiagnostics['dev_like'] ?? 'no')) ?></td>
          </tr>
          <tr>
            <th>Host detectado</th>
            <td><code><?= h((string) ($buildDiagnostics['http_host'] ?? '(vacío)')) ?></code></td>
          </tr>
          <tr>
            <th>URL de publicación</th>
            <td><code><?= h((string) ($buildDiagnostics['publish_url'] ?? '(sin configurar)')) ?></code></td>
          </tr>
          <tr>
            <th>Versión actual</th>
            <td><code><?= h((string) ($buildDiagnostics['current_version'] ?? '-')) ?></code></td>
          </tr>
          <tr>
            <th>Última versión remota</th>
            <td><code><?= h((string) ($buildDiagnostics['latest_remote'] ?? '-')) ?></code></td>
          </tr>
        </tbody>
      </table>
    </div>
  </section>

  <?php if ($buildEnabled): ?>
  <section class="card">
    <div class="card-title">Generar paquete core-update desde esta instalación</div>
    <p class="updates-meta updates-intro-note"><?= h($buildEnabledReason) ?></p>
    <form method="post" class="updates-form-grid">
      <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
      <div class="form-group">
        <label>Versión destino del paquete</label>
        <input type="text" name="build_version" class="form-control" value="<?= h(updates_next_build_version()) ?>" readonly>
      </div>
      <div class="form-group">
        <label>Descripción</label>
        <textarea name="build_description" class="form-control" rows="3" placeholder="Resumen de cambios del núcleo"></textarea>
      </div>
      <div class="ui-checklist">
        <label class="ui-check-card"><input type="checkbox" name="include_docs" value="1"><span><strong>Incluir docs/</strong><br><span class="updates-meta">Incluye documentación del núcleo si quieres distribuirla.</span></span></label>
        <label class="ui-check-card"><input type="checkbox" name="include_guides" value="1" checked><span><strong>Incluir guías de actualización</strong><br><span class="updates-meta">Añade HTML y Markdown explicativos dentro del ZIP.</span></span></label>
        <label class="ui-check-card"><input type="checkbox" name="include_sql" value="1"><span><strong>Incluir migración SQL manual</strong><br><span class="updates-meta">Permite adjuntar SQL para actualizar estructura de BD sin perder datos.</span></span></label>
        <div class="ui-check-card"><span><strong>Exclusiones automáticas</strong><br><span class="updates-meta">Nunca incluye secretos, config local, theme-vars.css, logos, apps, plugins, vendor, updates, logs ni ZIPs previos.</span></span></div>
      </div>
      <div class="form-group updates-sql-group">
        <label>SQL opcional de migración</label>
        <textarea name="sql_content" class="form-control" rows="6" placeholder="ALTER TABLE ...;
UPDATE ...;"></textarea>
      </div>
      <div class="ui-actions">
        <button type="submit" name="action" value="generate" class="btn-primary-intranet">Generar paquete</button>
        <?php if ($publishEnabled): ?>
          <button type="submit" name="action" value="generate_and_publish" class="btn-primary-intranet">Generar y publicar en Store</button>
        <?php endif; ?>
      </div>
    </form>
  </section>

  <section class="card">
    <div class="updates-card-title">
      <div class="card-title updates-section-heading">Builds generadas</div>
      <span class="updates-badge"><?= count($builds) ?> build(s)</span>
    </div>
    <div class="table-responsive updates-table-wrap">
      <table class="table">
        <thead><tr><th>Fichero</th><th>Versión</th><th>Tamaño</th><th>Fecha</th></tr></thead>
        <tbody>
        <?php if (!$builds): ?>
          <tr><td colspan="4">No hay builds generadas todavía.</td></tr>
        <?php else: foreach ($builds as $b): ?>
          <tr>
            <td><a href="../../updates/core/builds/<?= rawurlencode((string) $b['file']) ?>" target="_blank"><?= h($b['file']) ?></a><br><span class="updates-meta"><?= h($b['description'] !== '' ? $b['description'] : 'Sin descripción') ?></span></td>
            <td><?= h($b['version'] !== '' ? $b['version'] : '-') ?></td>
            <td><?= h(updates_human_size((int) ($b['size'] ?? 0))) ?></td>
            <td><?= h(date('d/m/Y H:i', (int) ($b['mtime'] ?? time()))) ?></td>
          </tr>
        <?php endforeach; endif; ?>
        </tbody>
      </table>
    </div>
  </section>
  <?php else: ?>
  <section class="card">
    <div class="card-title">Generación de paquetes core-update</div>
    <p class="muted-help">Esta instalación solo puede consumir actualizaciones oficiales. La generación de paquetes del núcleo está deshabilitada aquí.</p>
    <p class="updates-meta"><?= h($buildEnabledReason) ?></p>
    <p class="updates-meta">Puedes activarla definiendo <code>CORE_UPDATE_BUILD_ENABLED</code> en <code>/etc/intranet/intranet-config.php</code> o en la ruta legacy <code>/etc/csif/intranet-config.php</code>. <code>common/config.php</code> queda solo como fallback temporal.</p>
  </section>
  <?php endif; ?>

  <section class="card">
    <div class="card-title">Subir paquete ZIP de actualización del núcleo</div>
    <form method="post" enctype="multipart/form-data">
      <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
      <input type="hidden" name="action" value="upload">
      <div class="form-group">
        <label>Selecciona un ZIP con <code>core-update.json</code></label>
        <input type="file" name="core_zip" class="form-control" accept=".zip" required>
      </div>
      <div class="ui-actions">
        <button type="submit" class="btn-primary-intranet">Subir paquete</button>
      </div>
    </form>
  </section>

  <section class="card">
    <div class="updates-card-title">
      <div class="card-title updates-section-heading">Paquetes locales</div>
      <span class="updates-badge"><?= count($localUpdates) ?> paquete(s)</span>
    </div>
    <?php if (empty($localUpdates)): ?>
      <p class="muted-help">Todavía no hay paquetes de actualización subidos localmente.</p>
    <?php else: ?>
      <div class="table-responsive updates-table-wrap">
        <table>
          <thead><tr><th>Versión</th><th>Nombre</th><th>Archivo</th><th>Tamaño</th><th>Acciones</th></tr></thead>
          <tbody>
          <?php foreach ($localUpdates as $pkg): ?>
            <tr>
              <td><strong><?= h((string)($pkg['version'] ?? '-')) ?></strong></td>
              <td><?= h((string)($pkg['name'] ?? 'Actualización del núcleo')) ?></td>
              <td><code><?= h((string)($pkg['_file'] ?? '')) ?></code></td>
              <td><?= h(updates_human_size((int)($pkg['_size'] ?? 0))) ?></td>
              <td>
                <form method="post" class="updates-inline-form">
                  <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
                  <input type="hidden" name="action" value="install_local">
                  <input type="hidden" name="package_file" value="<?= h((string)($pkg['_file'] ?? '')) ?>">
                  <div class="ui-checklist">
                    <label class="ui-check-card"><input type="checkbox" name="preserve_theme" checked> <span><strong>Conservar colores locales</strong><br><small class="updates-meta">Preserva <code>styles/css/theme-vars.css</code>.</small></span></label>
                    <label class="ui-check-card"><input type="checkbox" name="preserve_images" checked> <span><strong>Conservar logos e imágenes</strong><br><small class="updates-meta">Preserva <code>styles/images/</code>.</small></span></label>
                    <label class="ui-check-card"><input type="checkbox" checked disabled> <span><strong>Conservar apps locales</strong><br><small class="updates-meta">Siempre se preserva <code>apps/</code>.</small></span></label>
                    <label class="ui-check-card"><input type="checkbox" checked disabled> <span><strong>Conservar plugins locales</strong><br><small class="updates-meta">Siempre se preserva <code>plugins/</code>.</small></span></label>
                    <label class="ui-check-card"><input type="checkbox" name="apply_sql" checked> <span><strong>Actualizar estructura de BD</strong><br><small class="updates-meta">Ejecuta migraciones SQL del paquete sin borrar datos.</small></span></label>
                  </div>
                  <div class="ui-actions">
                    <button type="submit" class="btn primary" onclick="return confirm('Se creará un backup del núcleo y se aplicará la actualización. ¿Continuar?');">Actualizar con este paquete</button>
                  </div>
                </form>
                <form method="post" style="margin:0;" onsubmit="return confirm('¿Eliminar este paquete local?');">
                  <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
                  <input type="hidden" name="action" value="delete_local">
                  <input type="hidden" name="package_file" value="<?= h((string)($pkg['_file'] ?? '')) ?>">
                  <button type="submit" class="btn danger">Eliminar paquete</button>
                </form>
              </td>
            </tr>
          <?php endforeach; ?>
          </tbody>
        </table>
      </div>
    <?php endif; ?>
  </section>

  <section class="card">
    <div class="updates-card-title">
      <div class="card-title updates-section-heading">Actualizaciones publicadas en el Store</div>
      <span class="updates-badge"><?= count($remoteUpdates) ?> publicada(s)</span>
    </div>
    <?php if (empty($remoteUpdates)): ?>
      <p class="muted-help">No se han encontrado actualizaciones remotas del núcleo.</p>
    <?php else: ?>
      <div class="table-responsive updates-table-wrap">
        <table>
          <thead><tr><th>Versión</th><th>Nombre</th><th>Descripción</th><th>Acciones</th></tr></thead>
          <tbody>
          <?php foreach ($remoteUpdates as $pkg): ?>
            <?php $isNewer = !empty($pkg['version']) && version_compare((string)$pkg['version'], $currentVersion, '>'); ?>
            <tr>
              <td><strong><?= h((string)($pkg['version'] ?? '-')) ?></strong><?= $isNewer ? ' <span class="updates-badge">Nueva</span>' : '' ?></td>
              <td><?= h((string)($pkg['name'] ?? 'Actualización del núcleo')) ?></td>
              <td><?= h((string)($pkg['description'] ?? '')) ?></td>
              <td>
                <form method="post" class="updates-inline-form">
                  <input type="hidden" name="csrf" value="<?= h($csrf) ?>">
                  <input type="hidden" name="action" value="install_remote">
                  <input type="hidden" name="core_id" value="<?= h((string)($pkg['id'] ?? '')) ?>">
                  <div class="ui-checklist">
                    <label class="ui-check-card"><input type="checkbox" name="preserve_theme" checked> <span><strong>Conservar colores locales</strong></span></label>
                    <label class="ui-check-card"><input type="checkbox" name="preserve_images" checked> <span><strong>Conservar logos e imágenes</strong></span></label>
                    <label class="ui-check-card"><input type="checkbox" name="apply_sql" checked> <span><strong>Actualizar estructura de BD</strong></span></label>
                  </div>
                  <div class="ui-actions">
                    <button type="submit" class="btn primary" onclick="return confirm('Se descargará el paquete, se creará un backup y se aplicará la actualización. ¿Continuar?');">Descargar e instalar</button>
                  </div>
                </form>
              </td>
            </tr>
          <?php endforeach; ?>
          </tbody>
        </table>
      </div>
    <?php endif; ?>
  </section>

  <section class="card">
    <div class="card-title">Historial de actualizaciones aplicadas</div>
    <?php if (empty($log)): ?>
      <p class="muted-help">Aún no se ha registrado ninguna actualización del núcleo.</p>
    <?php else: ?>
      <div class="table-responsive updates-table-wrap">
        <table>
          <thead><tr><th>Fecha</th><th>Usuario</th><th>De</th><th>A</th><th>Backup</th><th>SQL</th></tr></thead>
          <tbody>
          <?php foreach ($log as $row): ?>
            <tr>
              <td><?= h((string)($row['applied_at'] ?? '')) ?></td>
              <td><?= h((string)($row['applied_by'] ?? '')) ?></td>
              <td><?= h((string)($row['version_from'] ?? '')) ?></td>
              <td><?= h((string)($row['version_to'] ?? '')) ?></td>
              <td><code><?= h((string)($row['backup_zip'] ?? '')) ?></code></td>
              <td><?= h((string)($row['sql_applied'] ?? '0')) ?></td>
            </tr>
          <?php endforeach; ?>
          </tbody>
        </table>
      </div>
    <?php endif; ?>
  </section>
</div>
</body>
</html>
