<?php
/**
 * Admin plugin for Tareas Masivas
 * Bulk task upload from Excel/CSV files
 *
 * Formato simplificado:
 * año | cliente | codigo proyecto | tipo | descripcion | enero | febrero | ... | diciembre
 */

use dokuwiki\Extension\AdminPlugin;

class admin_plugin_taskmassivas extends AdminPlugin {

    protected $helper;
    protected $uploadResult = null;
    protected $uploadFilename = '';
    protected $uploadPreview = false;

    public function __construct() {
        $this->helper = plugin_load('helper', 'taskmassivas');
    }

    public function forAdminOnly() {
        return false;
    }

    protected function isManager() {
        global $INPUT, $USERINFO;

        // Check native DokuWiki permissions
        if (auth_isadmin() || auth_ismanager()) {
            return true;
        }

        // Check grupo "director" — acceso total a carga masiva
        if (!empty($USERINFO['grps']) && in_array('director', $USERINFO['grps'])) {
            return true;
        }

        // Check virtual manager (centralizado en managerprotect)
        $username = $INPUT->server->str('REMOTE_USER');
        if (!empty($username) && action_plugin_managerprotect::shouldHaveManagerGroup($username)) {
            return true;
        }

        return false;
    }

    public function getMenuSort() {
        return 16;
    }

    /**
     * Ocultar el menú para usuarios sin permisos
     * (evita que usuarios normales vean un ítem que no pueden usar)
     */
    public function showInMenu() {
        return $this->isManager();
    }

    public function getMenuText($language) {
        return $this->getLang('menu');
    }

    public function getMenuIcon() {
        return DOKU_INC . 'lib/images/menu/upload.svg';
    }

    public function handle() {
        global $INPUT;

        if (!$this->isManager()) {
            return;
        }

        $action = $INPUT->str('action');

        if (!empty($action) && !checkSecurityToken()) {
            return;
        }

        switch ($action) {
            case 'upload_tasks':
                $this->handleUploadTasks();
                break;

            case 'confirm_upload':
                $this->handleConfirmUpload();
                break;

            case 'download_template':
                $this->handleDownloadTemplate();
                break;

            case 'revert_upload':
                $this->handleRevertUpload();
                break;

            case 'clear_logs':
                $this->handleClearLogs();
                break;
        }
    }

    public function html() {
        global $INPUT;

        if (!$this->isManager()) {
            echo '<div class="error">';
            echo '<p>' . $this->getLang('error_no_permission') . '</p>';
            echo '</div>';
            return;
        }

        $this->renderStyles();

        echo '<div class="taskmassivas-admin">';
        echo '<h1>' . $this->getLang('admin_title') . '</h1>';

        // Tabs
        $tab = $INPUT->str('tab', 'upload');
        $this->renderTabs($tab);

        echo '<div class="tab-content">';

        switch ($tab) {
            case 'log':
                $this->renderLogView();
                break;
            case 'upload':
            default:
                $this->renderUploadForm();
                break;
        }

        echo '</div>'; // tab-content
        echo '</div>'; // taskmassivas-admin
    }

    protected function renderTabs($activeTab) {
        global $ID;

        $tabs = [
            'upload' => $this->getLang('upload_tasks'),
            'log' => $this->getLang('view_log')
        ];

        echo '<div class="tabs">';
        foreach ($tabs as $tabId => $tabLabel) {
            $class = ($tabId === $activeTab) ? 'active' : '';
            $url = wl($ID, ['do' => 'admin', 'page' => 'taskmassivas', 'tab' => $tabId]);
            echo '<a href="' . $url . '" class="tab ' . $class . '">' . $tabLabel . '</a>';
        }
        echo '</div>';
    }

    protected function renderUploadForm() {
        global $ID, $INPUT;

        echo '<h2>' . $this->getLang('upload_title') . '</h2>';

        // Info box with template download
        echo '<div class="info-box">';
        echo '<p>' . $this->getLang('upload_info') . '</p>';
        echo '<a href="' . wl($ID, ['do' => 'admin', 'page' => 'taskmassivas', 'action' => 'download_template', 'sectok' => getSecurityToken()]) . '" class="btn-secondary">';
        echo $this->getLang('download_template');
        echo '</a>';
        echo '</div>';

        // Template format info
        echo '<div class="template-info">';
        echo '<strong>Formato del Template:</strong>';
        echo '<table class="format-table" style="width: 100%; margin-top: 10px; font-size: 0.9em;">';
        echo '<tr><th style="text-align:left; padding: 5px; background: #f1f5f9;">Columna</th><th style="text-align:left; padding: 5px; background: #f1f5f9;">Descripcion</th></tr>';
        echo '<tr><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><code>Año</code></td><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;">Año para las tareas (ej: 2026)</td></tr>';
        echo '<tr><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><code>Cliente</code></td><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;">Nombre del cliente (informativo)</td></tr>';
        echo '<tr><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><code>Codigo del proyecto - sie</code></td><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><strong>ID del proyecto destino</strong> (ej: 1, 2, 123)</td></tr>';
        echo '<tr><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><code>Tipo</code></td><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;">Critica, Importante, Acción correctiva, Preventiva, Programada, Urgente</td></tr>';
        echo '<tr><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><code>Descripcion de la tarea</code></td><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><strong>Contenido de la tarea</strong> (obligatorio)</td></tr>';
        echo '<tr><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><code>Responsable</code></td><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><strong>Usuario asignado</strong> (opcional, ver tabla abajo)</td></tr>';
        echo '<tr><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><code>enero...diciembre</code></td><td style="padding: 5px; border-bottom: 1px solid #e2e8f0;"><strong>Dia del mes</strong> para programar la tarea (ej: 15, 20, 31)</td></tr>';
        echo '</table>';

        echo '<p style="margin-top: 10px; color: #64748b; font-size: 0.9em;">';
        echo '<strong>Nota:</strong> Cada fila puede generar múltiples tareas (una por mes con fecha). ';
        echo 'Para crear varias tareas en un mismo mes, separe los días con coma (ej: <code>5,20</code> en la columna "marzo" genera 2 tareas).';
        echo '</p>';
        echo '</div>';

        // Users list panel
        $this->renderUsersPanel();

        // Check for upload results
        if ($this->uploadResult !== null) {
            $this->renderUploadResult();
            return;
        }

        // Upload form - SIMPLIFIED (no project selector needed)
        echo '<form method="post" enctype="multipart/form-data" class="upload-form" onsubmit="return taskmassivas_confirmSubmit(this);">';
        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '">';
        echo '<input type="hidden" name="action" value="upload_tasks">';

        // File input
        echo '<div class="form-group">';
        echo '<label for="taskfile">' . $this->getLang('select_file') . ' *</label>';
        echo '<input type="file" name="taskfile" id="taskfile" accept=".xlsx,.csv" required>';
        echo '<p style="margin-top: 5px; color: #64748b; font-size: 0.9em;">Formatos aceptados: Excel (.xlsx) o CSV</p>';
        echo '</div>';

        // Preview checkbox + Submit button (inline)
        echo '<div class="form-group" style="display: flex; align-items: flex-start; justify-content: space-between; gap: 20px;">';

        // Left side: checkbox
        echo '<div class="checkbox">';
        echo '<label>';
        echo '<input type="checkbox" name="preview_only" value="1" checked>';
        echo ' ' . $this->getLang('preview_only');
        echo '</label>';
        echo '<p style="margin-left: 24px; color: #64748b; font-size: 0.9em; margin-top: 4px;">Recomendado: revise primero la vista previa antes de crear las tareas</p>';
        echo '</div>';

        // Right side: submit button
        echo '<div>';
        echo '<input type="submit" value="' . $this->getLang('btn_upload') . '" style="background:#3b82f6;color:white;border:none;outline:none;box-shadow:none;padding:10px 20px;border-radius:6px;font-size:1em;font-weight:500;cursor:pointer;-webkit-appearance:none;-moz-appearance:none;appearance:none;">';
        echo '</div>';

        echo '</div>';

        echo '</form>';
    }

    protected function handleUploadTasks() {
        global $INPUT;

        $previewOnly = $INPUT->bool('preview_only');
        $userId = $INPUT->server->str('REMOTE_USER');

        // Validate file
        if (empty($_FILES['taskfile']['tmp_name'])) {
            msg($this->getLang('error_no_file'), -1);
            return;
        }

        // Validar errores de upload PHP
        if ($_FILES['taskfile']['error'] !== UPLOAD_ERR_OK) {
            $uploadErrors = [
                UPLOAD_ERR_INI_SIZE => 'El archivo excede el límite del servidor',
                UPLOAD_ERR_FORM_SIZE => 'El archivo excede el límite del formulario',
                UPLOAD_ERR_PARTIAL => 'El archivo se subió parcialmente',
                UPLOAD_ERR_NO_FILE => 'No se seleccionó archivo',
                UPLOAD_ERR_NO_TMP_DIR => 'Error del servidor (sin directorio temporal)',
                UPLOAD_ERR_CANT_WRITE => 'Error del servidor (no se puede escribir)',
            ];
            $errMsg = isset($uploadErrors[$_FILES['taskfile']['error']])
                ? $uploadErrors[$_FILES['taskfile']['error']]
                : 'Error desconocido al subir archivo';
            msg($errMsg, -1);
            return;
        }

        $tmpFile = $_FILES['taskfile']['tmp_name'];
        $originalName = $_FILES['taskfile']['name'];
        $ext = strtolower(pathinfo($originalName, PATHINFO_EXTENSION));

        if (!in_array($ext, ['xlsx', 'csv'])) {
            msg($this->getLang('error_invalid_file'), -1);
            return;
        }

        // Límite de tamaño: 5MB máximo
        $maxSize = 5 * 1024 * 1024;
        if ($_FILES['taskfile']['size'] > $maxSize) {
            msg('El archivo excede el límite de 5MB. Divida el archivo en partes más pequeñas.', -1);
            return;
        }

        // Import tasks
        require_once(DOKU_PLUGIN . 'taskmassivas/lib/TaskImporter.php');
        $importer = new \dokuwiki\plugin\taskmassivas\lib\TaskImporter($this->helper);

        $result = $importer->import($tmpFile, $previewOnly, $userId, $originalName);

        // Store result in class properties for display
        $this->uploadResult = $result;
        $this->uploadFilename = $originalName;
        $this->uploadPreview = $previewOnly;

        // Si es preview exitoso, guardar archivo temporal para confirmar sin re-upload
        if ($previewOnly && $result['stats']['valid'] > 0) {
            $cacheDir = $this->getCacheDir();
            if ($cacheDir) {
                $userId = $INPUT->server->str('REMOTE_USER');
                $cachedFile = $cacheDir . '/' . md5($userId . session_id()) . '.' . $ext;
                @copy($tmpFile, $cachedFile);
                $this->uploadResult['cached_file'] = $cachedFile;
                $this->uploadResult['cached_name'] = $originalName;
            }
        }

        // Log if not preview and tasks were created
        if (!$previewOnly && $result['stats']['created'] > 0) {
            // Extract task IDs from result
            $taskIds = [];
            foreach ($result['tasks'] as $task) {
                if (!empty($task['task_id'])) {
                    $taskIds[] = $task['task_id'];
                }
            }

            // Log with project_id = 0 since tasks can span multiple projects
            $this->helper->logUpload(
                0, // Multiple projects
                $originalName,
                $result['stats']['created'],
                $result['stats']['errors'],
                count($result['errors']),
                $userId,
                $taskIds
            );
        }
    }

    protected function handleRevertUpload() {
        global $INPUT;

        // Validar que sea POST (evitar CSRF via link GET)
        if ($INPUT->server->str('REQUEST_METHOD') !== 'POST') {
            msg('Acción no permitida via GET. Use el formulario.', -1);
            return;
        }

        $logId = $INPUT->int('log_id');
        $userId = $INPUT->server->str('REMOTE_USER');

        if (!$logId) {
            msg($this->getLang('error_invalid_log'), -1);
            return;
        }

        $result = $this->helper->revertUpload($logId, $userId);

        if ($result['success']) {
            msg($result['message'], 1);
        } else {
            msg($result['message'], -1);
        }
    }

    protected function handleClearLogs() {
        global $INPUT;

        // Validar que sea POST
        if ($INPUT->server->str('REQUEST_METHOD') !== 'POST') {
            msg('Acción no permitida via GET. Use el formulario.', -1);
            return;
        }

        $clearAll = $INPUT->bool('clear_all');
        $deleted = $this->helper->clearLogs(!$clearAll);

        if ($deleted > 0) {
            msg("Se eliminaron {$deleted} registros del historial.", 1);
        } else {
            msg("No hay registros para eliminar.", 0);
        }
    }

    /**
     * Confirmar upload desde archivo cacheado (sin re-upload)
     */
    protected function handleConfirmUpload() {
        global $INPUT;

        if ($INPUT->server->str('REQUEST_METHOD') !== 'POST') {
            msg('Acción no permitida via GET.', -1);
            return;
        }

        $userId = $INPUT->server->str('REMOTE_USER');
        $cacheDir = $this->getCacheDir();
        if (!$cacheDir) {
            msg('Error interno: directorio de cache no disponible.', -1);
            return;
        }

        // Validar extensión (whitelist estricta — evitar path traversal)
        $cachedExt = $INPUT->str('cached_ext', 'xlsx');
        if (!in_array($cachedExt, ['xlsx', 'csv'], true)) {
            msg('Extensión de archivo no válida.', -1);
            return;
        }
        $cachedFile = $cacheDir . '/' . md5($userId . session_id()) . '.' . $cachedExt;

        // Sanitizar nombre original (solo para display/log, no para paths)
        $originalName = basename($INPUT->str('cached_name', 'archivo.xlsx'));

        if (!file_exists($cachedFile)) {
            msg('El archivo temporal ya no existe. Por favor suba el archivo nuevamente.', -1);
            return;
        }

        // Importar sin preview
        require_once(DOKU_PLUGIN . 'taskmassivas/lib/TaskImporter.php');
        $importer = new \dokuwiki\plugin\taskmassivas\lib\TaskImporter($this->helper);
        $result = $importer->import($cachedFile, false, $userId, $originalName);

        $this->uploadResult = $result;
        $this->uploadFilename = $originalName;
        $this->uploadPreview = false;

        // Log
        if ($result['stats']['created'] > 0) {
            $taskIds = [];
            foreach ($result['tasks'] as $task) {
                if (!empty($task['task_id'])) {
                    $taskIds[] = $task['task_id'];
                }
            }
            $this->helper->logUpload(0, $originalName, $result['stats']['created'], $result['stats']['errors'], count($result['errors']), $userId, $taskIds);
        }

        // Limpiar archivo cacheado
        @unlink($cachedFile);
    }

    /**
     * Obtener directorio de cache para archivos temporales
     * Usa data/tmp de DokuWiki (NO web-accesible, a diferencia del directorio del plugin)
     */
    protected function getCacheDir() {
        global $conf;

        // Preferir el tmpdir de DokuWiki (data/tmp/ — protegido de acceso web)
        $base = !empty($conf['tmpdir']) ? $conf['tmpdir'] : (DOKU_INC . 'data/tmp');
        $dir = $base . '/taskmassivas_cache';

        if (!is_dir($dir)) {
            @mkdir($dir, 0775, true);
        }

        // Limpiar archivos cacheados con más de 1 hora (evitar acumulación)
        $this->cleanCacheDir($dir, 3600);

        return is_dir($dir) ? $dir : null;
    }

    /**
     * Limpiar archivos de cache antiguos
     * @param string $dir Directorio de cache
     * @param int $maxAge Edad máxima en segundos (default 1 hora)
     */
    protected function cleanCacheDir($dir, $maxAge = 3600) {
        if (!is_dir($dir)) return;

        $now = time();
        $files = @scandir($dir);
        if (!$files) return;

        foreach ($files as $file) {
            if ($file === '.' || $file === '..') continue;
            $path = $dir . '/' . $file;
            if (is_file($path) && ($now - filemtime($path)) > $maxAge) {
                @unlink($path);
            }
        }
    }

    protected function renderUploadResult() {
        if ($this->uploadResult === null) {
            return;
        }

        $result = $this->uploadResult;
        $filename = $this->uploadFilename;
        $preview = $this->uploadPreview;

        $title = $preview ? $this->getLang('upload_preview') : $this->getLang('upload_result');
        echo '<h3>' . $title . '</h3>';

        // Summary
        echo '<div class="result-summary">';
        echo '<p><strong>Archivo:</strong> ' . hsc($filename) . '</p>';
        echo '<div class="stats">';
        echo '<span class="stat"><strong>Filas procesadas:</strong> ' . intval($result['stats']['total_rows']) . '</span>';
        echo '<span class="stat"><strong>Tareas generadas:</strong> ' . intval($result['stats']['total_tasks']) . '</span>';
        echo '<span class="stat ok"><strong>' . ($preview ? 'Validas' : $this->getLang('tasks_created')) . ':</strong> ' . intval($preview ? $result['stats']['valid'] : $result['stats']['created']) . '</span>';
        echo '<span class="stat error"><strong>' . $this->getLang('tasks_errors') . ':</strong> ' . intval($result['stats']['errors']) . '</span>';
        echo '</div>';
        echo '</div>';

        // Detailed error summary grouped by type
        if (!empty($result['errors']) || !empty($result['warnings'])) {
            echo '<div class="detailed-errors">';
            echo '<h4 style="color: #b45309; margin-top: 20px;">Detalle de problemas encontrados:</h4>';

            // Group errors by type
            $errorTypes = [
                'no_content' => ['label' => 'Sin descripcion de tarea', 'items' => []],
                'no_project' => ['label' => 'Proyecto no encontrado', 'items' => []],
                'invalid_date' => ['label' => 'Fecha invalida', 'items' => []],
                'no_months' => ['label' => 'Sin fechas en columnas de meses', 'items' => []],
                'other' => ['label' => 'Otros errores', 'items' => []]
            ];

            foreach ($result['errors'] as $error) {
                $categorized = false;
                if (stripos($error, 'descripcion') !== false || stripos($error, 'contenido') !== false) {
                    $errorTypes['no_content']['items'][] = $error;
                    $categorized = true;
                }
                if (stripos($error, 'proyecto') !== false) {
                    $errorTypes['no_project']['items'][] = $error;
                    $categorized = true;
                }
                if (stripos($error, 'fecha') !== false && stripos($error, 'invalido') !== false) {
                    $errorTypes['invalid_date']['items'][] = $error;
                    $categorized = true;
                }
                if (!$categorized) {
                    $errorTypes['other']['items'][] = $error;
                }
            }

            // Render grouped errors
            foreach ($errorTypes as $type => $data) {
                if (!empty($data['items'])) {
                    $bgColor = ($type === 'no_project') ? '#fef2f2' : '#fef3c7';
                    $borderColor = ($type === 'no_project') ? '#fecaca' : '#fcd34d';
                    echo '<div style="margin: 10px 0; padding: 10px; background: ' . $bgColor . '; border: 1px solid ' . $borderColor . '; border-radius: 6px;">';
                    echo '<strong>' . hsc($data['label']) . ' (' . count($data['items']) . '):</strong>';
                    echo '<ul style="margin: 5px 0 0 20px; font-size: 0.9em;">';
                    foreach (array_slice($data['items'], 0, 5) as $item) {
                        echo '<li>' . hsc($item) . '</li>';
                    }
                    if (count($data['items']) > 5) {
                        echo '<li>... y ' . (count($data['items']) - 5) . ' mas</li>';
                    }
                    echo '</ul>';
                    echo '</div>';
                }
            }

            // Warnings
            if (!empty($result['warnings'])) {
                echo '<div style="margin: 10px 0; padding: 10px; background: #dbeafe; border: 1px solid #93c5fd; border-radius: 6px;">';
                echo '<strong>Advertencias (' . count($result['warnings']) . '):</strong>';
                echo '<ul style="margin: 5px 0 0 20px; font-size: 0.9em;">';
                foreach (array_slice($result['warnings'], 0, 5) as $warn) {
                    echo '<li>' . hsc($warn) . '</li>';
                }
                if (count($result['warnings']) > 5) {
                    echo '<li>... y ' . (count($result['warnings']) - 5) . ' mas</li>';
                }
                echo '</ul>';
                echo '</div>';
            }

            echo '</div>';
        }

        // Tasks table
        if (!empty($result['tasks'])) {
            echo '<table class="tasks-table">';
            echo '<thead><tr>';
            echo '<th>Fila</th>';
            echo '<th>Proyecto</th>';
            echo '<th>Mes</th>';
            echo '<th>Fecha</th>';
            echo '<th>Responsable</th>';
            echo '<th>Descripcion</th>';
            echo '<th>Estado</th>';
            echo '</tr></thead>';
            echo '<tbody>';

            foreach ($result['tasks'] as $task) {
                $rowClass = $task['valid'] ? 'valid' : 'invalid';
                echo '<tr class="' . $rowClass . '">';
                echo '<td>' . intval($task['row']) . '</td>';
                echo '<td>#' . intval($task['project_id']) . '</td>';
                echo '<td>' . hsc($task['month_name']) . '</td>';
                echo '<td>' . hsc($task['plan_date']) . '</td>';
                echo '<td>';
                if (!empty($task['assignee'])) {
                    echo '<code style="background: #e0f2fe; padding: 2px 6px; border-radius: 4px; color: #0369a1;">' . hsc($task['assignee']) . '</code>';
                } else {
                    echo '<span style="color: #94a3b8; font-style: italic;">usuario actual</span>';
                }
                echo '</td>';
                // Show original description (content_raw) in preview, not the formatted table
                $displayContent = isset($task['content_raw']) ? $task['content_raw'] : $task['content'];
                echo '<td>' . hsc(mb_substr($displayContent, 0, 50)) . (mb_strlen($displayContent) > 50 ? '...' : '') . '</td>';
                echo '<td>';
                if ($task['valid']) {
                    if (!$preview && isset($task['task_id'])) {
                        echo '<span class="status ok">OK (ID: ' . intval($task['task_id']) . ')</span>';
                    } else {
                        echo '<span class="status ok">' . $this->getLang('status_ok') . '</span>';
                    }
                    // Show warning if any
                    if (!empty($task['warnings'])) {
                        echo '<br><small style="color: #b45309;">' . hsc(implode(', ', $task['warnings'])) . '</small>';
                    }
                } else {
                    echo '<span class="status error">' . hsc(implode(', ', $task['errors'])) . '</span>';
                }
                echo '</td>';
                echo '</tr>';
            }

            echo '</tbody></table>';
        }

        // Action buttons
        echo '<div class="result-actions">';
        if ($preview && $result['stats']['valid'] > 0) {
            // Si hay archivo cacheado, mostrar botón de confirmación directa
            if (!empty($result['cached_file']) && file_exists($result['cached_file'])) {
                $ext = pathinfo($result['cached_file'], PATHINFO_EXTENSION);
                echo '<div style="background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 15px 20px; margin-bottom: 15px;">';
                echo '<p style="color: #166534; font-weight: 600; margin: 0 0 10px 0;">✓ ' . intval($result['stats']['valid']) . ' tareas válidas listas para crear</p>';
                echo '<form method="post" style="display:inline;" onsubmit="return confirm(\'¿Crear ' . intval($result['stats']['valid']) . ' tareas?\\n\\nEsta acción no se puede deshacer fácilmente.\');">';
                echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '">';
                echo '<input type="hidden" name="action" value="confirm_upload">';
                echo '<input type="hidden" name="cached_ext" value="' . hsc($ext) . '">';
                echo '<input type="hidden" name="cached_name" value="' . hsc($this->uploadFilename) . '">';
                echo '<input type="hidden" name="do" value="admin">';
                echo '<input type="hidden" name="page" value="taskmassivas">';
                echo '<button type="submit" style="background:#059669;color:white;border:none;padding:10px 24px;border-radius:6px;font-size:1em;font-weight:600;cursor:pointer;">Confirmar y Crear ' . intval($result['stats']['valid']) . ' Tareas</button>';
                echo '</form>';
                echo '</div>';
            } else {
                echo '<p style="color: #059669; font-weight: 500;">Se generarán ' . intval($result['stats']['valid']) . ' tareas. Para crearlas, vuelva a cargar el archivo sin marcar "Solo vista previa".</p>';
            }
        }
        echo '<a href="' . wl($GLOBALS['ID'], ['do' => 'admin', 'page' => 'taskmassivas']) . '" class="btn-secondary">Cargar otro archivo</a>';
        echo '</div>';
    }

    protected function renderUsersPanel() {
        $users = $this->helper->getUsers();

        echo '<div class="users-panel" style="margin-bottom: 20px;">';
        echo '<details>';
        echo '<summary style="cursor: pointer; padding: 10px 15px; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; font-weight: 500; color: #166534;">';
        echo '<span style="margin-left: 5px;">Ver usuarios disponibles para columna "responsable" (' . count($users) . ' usuarios)</span>';
        echo '</summary>';

        echo '<div style="padding: 15px; border: 1px solid #e2e8f0; border-top: none; border-radius: 0 0 8px 8px; background: #fafafa;">';

        if (empty($users)) {
            echo '<p style="color: #64748b; font-style: italic;">No se pudieron obtener los usuarios del sistema.</p>';
        } else {
            echo '<p style="margin-bottom: 10px; color: #64748b; font-size: 0.9em;">';
            echo 'Use el valor de la columna <strong>"Usuario"</strong> en su archivo Excel para asignar tareas.';
            echo '</p>';

            echo '<input type="text" class="taskmassivas-user-search" placeholder="Buscar usuario..." oninput="taskmassivas_filterUsers(this)">';
            echo '<span class="user-count" style="font-size: 0.85em; color: #94a3b8;"></span>';

            echo '<div style="max-height: 300px; overflow-y: auto;">';
            echo '<table style="width: 100%; border-collapse: collapse; font-size: 0.9em;">';
            echo '<thead style="position: sticky; top: 0; background: #f1f5f9;">';
            echo '<tr>';
            echo '<th style="text-align: left; padding: 8px 12px; border-bottom: 2px solid #e2e8f0; font-weight: 600;">Usuario</th>';
            echo '<th style="text-align: left; padding: 8px 12px; border-bottom: 2px solid #e2e8f0; font-weight: 600;">Nombre</th>';
            echo '</tr>';
            echo '</thead>';
            echo '<tbody>';

            foreach ($users as $user) {
                echo '<tr style="border-bottom: 1px solid #f1f5f9;">';
                echo '<td style="padding: 6px 12px;"><code style="background: #e0f2fe; padding: 2px 6px; border-radius: 4px; color: #0369a1;">' . hsc($user['username']) . '</code></td>';
                echo '<td style="padding: 6px 12px; color: #475569;">' . hsc($user['name']) . '</td>';
                echo '</tr>';
            }

            echo '</tbody>';
            echo '</table>';
            echo '</div>';
        }

        echo '</div>'; // content
        echo '</details>';
        echo '</div>'; // users-panel
    }

    protected function renderLogView() {
        global $ID;

        echo '<h2>' . $this->getLang('log_title') . '</h2>';

        $logs = $this->helper->getUploadLogs(50);

        if (empty($logs)) {
            echo '<p class="info">' . $this->getLang('no_logs') . '</p>';
            return;
        }

        // Mini-dashboard de estadísticas
        $totalCreated = 0;
        $totalReverted = 0;
        $totalUploads = count($logs);
        $activeUploads = 0;
        foreach ($logs as $log) {
            $totalCreated += intval($log['tasks_created']);
            if (!empty($log['reverted']) && $log['reverted'] == 1) {
                $totalReverted += intval($log['tasks_created']);
            } else {
                $activeUploads++;
            }
        }
        $netTasks = $totalCreated - $totalReverted;

        echo '<div style="display: flex; gap: 15px; margin-bottom: 20px; flex-wrap: wrap;">';

        echo '<div style="flex: 1; min-width: 150px; background: #eff6ff; border: 1px solid #bfdbfe; border-radius: 8px; padding: 15px; text-align: center;">';
        echo '<div style="font-size: 1.8em; font-weight: 700; color: #1e40af;">' . $totalUploads . '</div>';
        echo '<div style="color: #3b82f6; font-size: 0.9em;">Cargas totales</div>';
        echo '</div>';

        echo '<div style="flex: 1; min-width: 150px; background: #f0fdf4; border: 1px solid #bbf7d0; border-radius: 8px; padding: 15px; text-align: center;">';
        echo '<div style="font-size: 1.8em; font-weight: 700; color: #166534;">' . $netTasks . '</div>';
        echo '<div style="color: #059669; font-size: 0.9em;">Tareas activas netas</div>';
        echo '</div>';

        echo '<div style="flex: 1; min-width: 150px; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px; padding: 15px; text-align: center;">';
        echo '<div style="font-size: 1.8em; font-weight: 700; color: #475569;">' . $totalCreated . '</div>';
        echo '<div style="color: #64748b; font-size: 0.9em;">Total creadas</div>';
        echo '</div>';

        echo '<div style="flex: 1; min-width: 150px; background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 15px; text-align: center;">';
        echo '<div style="font-size: 1.8em; font-weight: 700; color: #991b1b;">' . $totalReverted . '</div>';
        echo '<div style="color: #dc2626; font-size: 0.9em;">Revertidas</div>';
        echo '</div>';

        echo '</div>';

        echo '<table class="log-table">';
        echo '<thead><tr>';
        echo '<th>' . $this->getLang('log_date') . '</th>';
        echo '<th>' . $this->getLang('log_file') . '</th>';
        echo '<th>' . $this->getLang('log_tasks') . '</th>';
        echo '<th>' . $this->getLang('log_user') . '</th>';
        echo '<th>Estado</th>';
        echo '<th>Tareas</th>';
        echo '<th>Acciones</th>';
        echo '</tr></thead>';
        echo '<tbody>';

        $taskThreadCache = []; // Cache task_id → thread_id para evitar N queries

        foreach ($logs as $log) {
            $isReverted = !empty($log['reverted']) && $log['reverted'] == 1;
            $canRevert = !$isReverted && !empty($log['task_ids']);

            echo '<tr' . ($isReverted ? ' style="opacity: 0.6;"' : '') . '>';
            echo '<td>' . hsc($log['uploaded_at']) . '</td>';
            echo '<td>' . hsc($log['filename']) . '</td>';
            echo '<td>' . intval($log['tasks_created']) . ' creadas, ' . intval($log['tasks_skipped']) . ' omitidas</td>';
            echo '<td>' . hsc($log['uploaded_by']) . '</td>';

            // Estado
            echo '<td>';
            if ($isReverted) {
                echo '<span style="color: #dc3545; font-weight: 500;">Revertida</span>';
                echo '<br><small style="color: #6c757d;">' . hsc($log['reverted_at']) . ' por ' . hsc($log['reverted_by']) . '</small>';
            } else {
                echo '<span style="color: #28a745;">Activa</span>';
            }
            echo '</td>';

            // Ver tareas (usar cache para evitar N queries)
            echo '<td>';
            if (!empty($log['task_ids'])) {
                $ids = json_decode($log['task_ids'], true);
                if (!empty($ids)) {
                    $firstId = intval($ids[0]);
                    // Cache: buscar thread_id solo si no lo tenemos ya
                    if (!isset($taskThreadCache[$firstId])) {
                        $db = $this->helper->getDB();
                        if ($db) {
                            $taskResult = $db->query("SELECT thread_id FROM task WHERE id = ?", $firstId);
                            $taskRow = $db->res2row($taskResult);
                            $taskThreadCache[$firstId] = $taskRow ? intval($taskRow['thread_id']) : null;
                        } else {
                            $taskThreadCache[$firstId] = null;
                        }
                    }
                    $threadId = $taskThreadCache[$firstId];

                    if ($threadId) {
                        $bezUrl = wl('', ['do' => 'admin', 'page' => 'bez', 'id' => $threadId]);
                        echo '<a href="' . $bezUrl . '" title="Ver proyecto #' . $threadId . '" style="color: #3b82f6; text-decoration: none; font-size: 0.9em;">';
                        echo count($ids) . ' tareas →';
                        echo '</a>';
                    } else {
                        echo '<span style="color: #94a3b8; font-size: 0.9em;">' . count($ids) . ' tareas</span>';
                    }
                }
            } else {
                echo '<span style="color: #94a3b8;">-</span>';
            }
            echo '</td>';

            // Acciones
            echo '<td>';
            if ($canRevert) {
                echo '<form method="post" style="display:inline;" onsubmit="return confirm(\'¿Está seguro de revertir esta carga?\\n\\nSe eliminarán ' . intval($log['tasks_created']) . ' tareas creadas.\');">';
                echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '">';
                echo '<input type="hidden" name="action" value="revert_upload">';
                echo '<input type="hidden" name="log_id" value="' . intval($log['id']) . '">';
                echo '<input type="hidden" name="do" value="admin">';
                echo '<input type="hidden" name="page" value="taskmassivas">';
                echo '<button type="submit" class="btn-revert">Revertir</button>';
                echo '</form>';
            } elseif ($isReverted) {
                echo '<span style="color: #6c757d;">-</span>';
            } else {
                echo '<span style="color: #6c757d;" title="No hay IDs de tareas registrados">No disponible</span>';
            }
            echo '</td>';

            echo '</tr>';
        }

        echo '</tbody></table>';

        // Botón para limpiar registros antiguos (POST para seguridad)
        echo '<div style="margin-top: 20px; padding-top: 15px; border-top: 1px solid #e2e8f0;">';
        echo '<form method="post" style="display:inline;" onsubmit="return confirm(\'¿Eliminar registros antiguos sin IDs de tareas?\\n\\nEstos registros no se pueden revertir.\');">';
        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '">';
        echo '<input type="hidden" name="action" value="clear_logs">';
        echo '<input type="hidden" name="do" value="admin">';
        echo '<input type="hidden" name="page" value="taskmassivas">';
        echo '<input type="hidden" name="tab" value="log">';
        echo '<button type="submit" class="btn-clear">Limpiar registros antiguos</button>';
        echo '</form>';
        echo '<span style="margin-left: 10px; color: #6c757d; font-size: 0.9em;">(elimina registros que no tienen IDs de tareas)</span>';
        echo '</div>';

        // Estilos
        echo '<style>
            .btn-revert {
                display: inline-block;
                padding: 5px 12px;
                background: #dc3545;
                color: white !important;
                text-decoration: none;
                border: none;
                border-radius: 4px;
                font-size: 0.85em;
                font-weight: 500;
                cursor: pointer;
            }
            .btn-revert:hover {
                background: #c82333;
            }
            .btn-clear {
                display: inline-block;
                padding: 8px 16px;
                background: #6c757d;
                color: white !important;
                text-decoration: none;
                border: none;
                border-radius: 4px;
                font-size: 0.9em;
                cursor: pointer;
            }
            .btn-clear:hover {
                background: #5a6268;
            }
        </style>';
    }

    protected function handleDownloadTemplate() {
        // First try the user's custom template
        $templateFile = DOKU_INC . 'template_tareas_masivas.xlsx';

        // If not found, use plugin's template
        if (!file_exists($templateFile)) {
            $templateFile = DOKU_PLUGIN . 'taskmassivas/templates/template_tareas.xlsx';
        }

        // If xlsx doesn't exist, try csv
        if (!file_exists($templateFile)) {
            $templateFile = DOKU_PLUGIN . 'taskmassivas/templates/template_tareas.csv';
        }

        if (!file_exists($templateFile)) {
            msg($this->getLang('error_template_not_found'), -1);
            return;
        }

        $ext = pathinfo($templateFile, PATHINFO_EXTENSION);
        $mimeType = ($ext === 'xlsx')
            ? 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
            : 'text/csv';

        // Limpiar cualquier output buffer de DokuWiki antes de enviar el archivo
        while (ob_get_level() > 0) {
            ob_end_clean();
        }

        header('Content-Type: ' . $mimeType);
        header('Content-Disposition: attachment; filename="template_tareas_masivas.' . $ext . '"');
        header('Content-Length: ' . filesize($templateFile));
        header('Cache-Control: no-cache, must-revalidate');
        header('Pragma: no-cache');

        readfile($templateFile);
        exit;
    }

    protected function renderStyles() {
        echo '<style>
.taskmassivas-admin {
    max-width: 1200px;
    margin: 0 auto;
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
}

.taskmassivas-admin h1 {
    color: #1e293b;
    margin-bottom: 20px;
}

.tabs {
    display: flex;
    gap: 5px;
    margin-bottom: 20px;
    border-bottom: 2px solid #e2e8f0;
}

.tab {
    padding: 10px 20px;
    text-decoration: none;
    color: #64748b;
    border-bottom: 2px solid transparent;
    margin-bottom: -2px;
    transition: all 0.2s;
}

.tab:hover {
    color: #3b82f6;
}

.tab.active {
    color: #3b82f6;
    border-bottom-color: #3b82f6;
    font-weight: 600;
}

.info-box {
    background: #eff6ff;
    border: 1px solid #bfdbfe;
    border-radius: 8px;
    padding: 15px 20px;
    margin-bottom: 20px;
}

.info-box p {
    margin: 0 0 10px 0;
}

.template-info {
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 15px 20px;
    margin-bottom: 20px;
}

.template-info code {
    background: #e2e8f0;
    padding: 2px 6px;
    border-radius: 4px;
    font-size: 0.9em;
}

.btn-primary, .btn-secondary {
    display: inline-block;
    padding: 10px 20px;
    border-radius: 6px;
    text-decoration: none;
    font-weight: 500;
    cursor: pointer;
    border: none;
    transition: all 0.2s;
}

.taskmassivas-admin .btn-primary,
.taskmassivas-admin button.btn-primary,
.taskmassivas-admin .form-actions button {
    background: #3b82f6 !important;
    color: white !important;
    border: none !important;
    outline: none !important;
    box-shadow: none !important;
    -webkit-appearance: none;
    -moz-appearance: none;
    appearance: none;
    padding: 10px 20px !important;
    border-radius: 6px !important;
    font-size: 1em !important;
    font-weight: 500;
    cursor: pointer;
}

.taskmassivas-admin .btn-primary:hover,
.taskmassivas-admin button.btn-primary:hover {
    background: #2563eb !important;
}

.btn-secondary {
    background: #f1f5f9;
    color: #475569;
    border: 1px solid #e2e8f0;
}

.btn-secondary:hover {
    background: #e2e8f0;
}

.upload-form {
    background: white;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 25px;
}

.form-group {
    margin-bottom: 20px;
}

.form-group label {
    display: block;
    margin-bottom: 8px;
    font-weight: 500;
    color: #374151;
}

.form-group select,
.form-group input[type="file"] {
    width: 100%;
    padding: 10px;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    font-size: 1em;
}

.form-group.checkbox label {
    display: flex;
    align-items: center;
    gap: 8px;
    cursor: pointer;
}

.form-actions {
    margin-top: 25px;
    padding-top: 20px;
    border-top: 1px solid #e2e8f0;
}

.result-summary {
    background: #f8fafc;
    border: 1px solid #e2e8f0;
    border-radius: 8px;
    padding: 20px;
    margin-bottom: 20px;
}

.stats {
    display: flex;
    flex-wrap: wrap;
    gap: 15px;
    margin-top: 15px;
}

.stat {
    padding: 8px 15px;
    background: #e2e8f0;
    border-radius: 20px;
    font-size: 0.9em;
}

.stat.ok {
    background: #dcfce7;
    color: #166534;
}

.stat.error {
    background: #fef2f2;
    color: #991b1b;
}

.tasks-table, .log-table {
    width: 100%;
    border-collapse: collapse;
    margin: 20px 0;
    background: white;
    border-radius: 8px;
    overflow: hidden;
    box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

.tasks-table th, .log-table th {
    background: #f1f5f9;
    padding: 12px 15px;
    text-align: left;
    font-weight: 600;
    color: #374151;
    border-bottom: 2px solid #e2e8f0;
}

.tasks-table td, .log-table td {
    padding: 10px 15px;
    border-bottom: 1px solid #f1f5f9;
}

.tasks-table tr.invalid {
    background: #fef2f2;
}

.tasks-table tr.valid:hover, .log-table tr:hover {
    background: #f8fafc;
}

.status {
    display: inline-block;
    padding: 3px 10px;
    border-radius: 12px;
    font-size: 0.85em;
    font-weight: 500;
}

.status.ok {
    background: #dcfce7;
    color: #166534;
}

.status.error {
    background: #fef2f2;
    color: #991b1b;
}

.result-actions {
    margin-top: 25px;
    padding-top: 20px;
    border-top: 1px solid #e2e8f0;
}

.result-actions p {
    margin-bottom: 15px;
}

p.info {
    color: #64748b;
    font-style: italic;
}

/* Spinner overlay */
.taskmassivas-spinner-overlay {
    display: none;
    position: fixed;
    top: 0; left: 0; right: 0; bottom: 0;
    background: rgba(255,255,255,0.85);
    z-index: 9999;
    justify-content: center;
    align-items: center;
    flex-direction: column;
}
.taskmassivas-spinner-overlay.active {
    display: flex;
}
.taskmassivas-spinner {
    width: 48px; height: 48px;
    border: 4px solid #e2e8f0;
    border-top-color: #3b82f6;
    border-radius: 50%;
    animation: tm-spin 0.8s linear infinite;
}
@keyframes tm-spin { to { transform: rotate(360deg); } }
.taskmassivas-spinner-text {
    margin-top: 15px;
    font-size: 1.1em;
    color: #475569;
    font-weight: 500;
}

/* User search filter */
.taskmassivas-user-search {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid #d1d5db;
    border-radius: 6px;
    font-size: 0.9em;
    margin-bottom: 10px;
    box-sizing: border-box;
}
.taskmassivas-user-search:focus {
    outline: none;
    border-color: #3b82f6;
    box-shadow: 0 0 0 2px rgba(59,130,246,0.15);
}
</style>';

        // JavaScript: confirmación + spinner + filtro usuarios
        echo '<script>
function taskmassivas_confirmSubmit(form) {
    var previewCb = form.querySelector("input[name=preview_only]");
    var isPreview = previewCb && previewCb.checked;
    var fileInput = form.querySelector("input[name=taskfile]");

    if (!fileInput || !fileInput.value) {
        return true; // HTML5 required se encarga
    }

    if (!isPreview) {
        if (!confirm("¿Está seguro?\\n\\nLas tareas se crearán directamente sin vista previa.\\nSe recomienda usar vista previa primero.")) {
            return false;
        }
    }

    // Mostrar spinner
    var overlay = document.getElementById("taskmassivas-spinner");
    if (overlay) {
        var txt = overlay.querySelector(".taskmassivas-spinner-text");
        if (txt) txt.textContent = isPreview ? "Analizando archivo..." : "Creando tareas...";
        overlay.classList.add("active");
    }

    // Deshabilitar botón para evitar doble click
    var btn = form.querySelector("input[type=submit], button[type=submit]");
    if (btn) { btn.disabled = true; btn.style.opacity = "0.6"; }

    return true;
}

function taskmassivas_filterUsers(input) {
    var filter = input.value.toLowerCase();
    var table = input.closest(".users-panel").querySelector("tbody");
    if (!table) return;
    var rows = table.querySelectorAll("tr");
    var visible = 0;
    for (var i = 0; i < rows.length; i++) {
        var text = rows[i].textContent.toLowerCase();
        var show = !filter || text.indexOf(filter) > -1;
        rows[i].style.display = show ? "" : "none";
        if (show) visible++;
    }
    var counter = input.parentNode.querySelector(".user-count");
    if (counter) counter.textContent = visible + " encontrados";
}
</script>';

        // Spinner overlay HTML
        echo '<div id="taskmassivas-spinner" class="taskmassivas-spinner-overlay">';
        echo '<div class="taskmassivas-spinner"></div>';
        echo '<div class="taskmassivas-spinner-text">Procesando...</div>';
        echo '</div>';
    }
}
