<?php

use dokuwiki\Extension\AdminPlugin;

/**
 * Database Cleanup Manager - Limpieza completa de la base de datos de proyectos
 *
 * Plugin para limpiar toda la base de datos de proyectos, incluyendo:
 * - Proyectos (threads)
 * - Tareas (tasks)
 * - Comentarios
 * - Participantes
 * - Archivos wiki de proyectos
 * - Permisos ACL de proyectos
 */
class admin_plugin_dbcleanup extends AdminPlugin
{
    /** @var helper_plugin_sqlite */
    protected $sqlite = null;

    /** @var bool Flag para saber si ya intentamos conectar */
    protected $db_initialized = false;

    /** @var string Mensaje de estado */
    protected $message = '';

    /** @var string Tipo de mensaje (success/error) */
    protected $message_type = '';

    /** @var array Log de operaciones */
    protected $log = [];

    /**
     * Verifica la contraseña del usuario actual contra su password real.
     * Reemplaza la anterior CONFIRM_PASSWORD hardcodeada por seguridad.
     */
    protected function verifyUserPassword($password)
    {
        global $auth;
        $user = $_SERVER['REMOTE_USER'] ?? '';
        if (empty($user) || empty($password)) {
            return false;
        }
        return $auth->checkPass($user, $password);
    }

    /**
     * Inicializa la conexion a la base de datos
     */
    protected function getDatabase()
    {
        if ($this->db_initialized) {
            return $this->sqlite;
        }

        $this->db_initialized = true;

        // Intentar cargar directamente el plugin sqlite para evitar errores de bez_db
        /** @var helper_plugin_sqlite $sqlite */
        $sqlite = plugin_load('helper', 'sqlite');
        if ($sqlite) {
            try {
                // Inicializar sin pasar por bez_db para evitar errores de migracion
                if ($sqlite->init('b3p', DOKU_PLUGIN . 'bez/db/')) {
                    $this->sqlite = $sqlite;
                }
            } catch (Exception $e) {
                $this->sqlite = null;
                $this->message = 'Error de base de datos: ' . $e->getMessage();
                $this->message_type = 'error';
            }
        }

        return $this->sqlite;
    }

    /**
     * Retorna el texto del menu de administracion
     */
    public function getMenuText($language)
    {
        return $this->getLang('menu');
    }

    /**
     * Orden en el menu de administracion (despues de projectacl=3)
     */
    public function getMenuSort()
    {
        return 4;
    }

    /**
     * Solo para superusuarios
     */
    public function forAdminOnly()
    {
        return true;
    }

    /**
     * Verifica si el usuario tiene permisos de manager, admin o director
     */
    protected function isManager()
    {
        global $INFO;

        // 1. Verificar permisos nativos de DokuWiki
        if (auth_isadmin() || auth_ismanager()) {
            return true;
        }

        // 2. Verificar si el usuario pertenece al grupo "director"
        $grps = isset($INFO['userinfo']['grps']) ? $INFO['userinfo']['grps'] : [];
        if (in_array('director', $grps)) {
            return true;
        }

        // 3. Verificar si es manager virtual (usuario comienza con "sie")
        if ($this->isVirtualManager()) {
            return true;
        }

        return false;
    }

    /**
     * Verifica si el usuario actual es un "manager virtual"
     * Un usuario es manager virtual si su nombre comienza con "sie" (case insensitive)
     */
    protected function isVirtualManager()
    {
        global $INPUT;

        $username = $INPUT->server->str('REMOTE_USER');
        if (empty($username)) {
            return false;
        }

        return action_plugin_managerprotect::shouldHaveManagerGroup($username);
    }

    /**
     * Icono del menu
     */
    public function getMenuIcon()
    {
        return DOKU_INC . 'lib/images/menu/settings.svg';
    }

    /**
     * Maneja las peticiones del usuario
     */
    public function handle()
    {
        global $INPUT;

        // Verificar permisos
        if (!$this->isManager()) {
            $this->message = $this->getLang('error_no_permission');
            $this->message_type = 'error';
            return;
        }

        // Procesar confirmacion final
        if ($INPUT->post->has('confirm_cleanup') && checkSecurityToken()) {
            $password = $INPUT->post->str('confirm_password');

            if (!$this->verifyUserPassword($password)) {
                $this->message = $this->getLang('error_invalid_password');
                $this->message_type = 'error';
                return;
            }

            // Ejecutar limpieza
            $this->executeCleanup();
        }
    }

    /**
     * Ejecuta el proceso de limpieza completo
     */
    protected function executeCleanup()
    {
        $this->log = [];

        // 1. Crear backup primero
        $this->addLog($this->getLang('process_backup'));
        $backupResult = $this->createBackup();

        if (!$backupResult) {
            $this->message = $this->getLang('process_backup_fail');
            $this->message_type = 'error';
            return;
        }
        $this->addLog($this->getLang('process_backup_success'));

        // 2. Obtener conexion a la base de datos
        $db = $this->getDatabase();
        if (!$db) {
            $this->message = $this->getLang('error_db_connection');
            $this->message_type = 'error';
            return;
        }

        try {
            // 3. Limpiar base de datos
            $this->addLog($this->getLang('process_cleaning'));

            // ============================================================
            // FASE 1: Tablas hoja (sin dependencias hacia ellas)
            // ============================================================
            $this->addLog($this->getLang('process_cleaning_tasks'));
            $db->query('DELETE FROM task_comment');
            $db->query('DELETE FROM task_participant');
            $db->query('DELETE FROM task_filename');
            $db->query('DELETE FROM bulk_task_log');

            $this->addLog($this->getLang('process_cleaning_threads'));
            $db->query('DELETE FROM thread_comment');
            $db->query('DELETE FROM thread_participant');
            $db->query('DELETE FROM thread_label');

            $db->query('DELETE FROM authentication_token');
            $db->query('DELETE FROM subscription');

            // ============================================================
            // FASE 2: Tablas padre (después de eliminar hijos)
            // ============================================================
            $db->query('DELETE FROM task');
            $db->query('DELETE FROM thread');
            $db->query('DELETE FROM progreso_proyecto');

            // ============================================================
            // FASE 3: Proyectos administrativos (admin_thread)
            // ============================================================
            $this->addLog($this->getLang('process_cleaning_admin_threads'));
            $db->query('DELETE FROM admin_thread_preferencia');
            $db->query('DELETE FROM admin_thread_progress');
            $db->query('DELETE FROM admin_thread');

            // ============================================================
            // FASE 4: Control administrativo (admin_control)
            // ============================================================
            $this->addLog($this->getLang('process_cleaning_admin_control'));
            $db->query('DELETE FROM admin_control_progress');
            $db->query('DELETE FROM admin_control_export_log');
            $db->query('DELETE FROM admin_control_items');
            $db->query('DELETE FROM admin_control_aplicadores');
            $db->query('DELETE FROM admin_control_groups');

            // ============================================================
            // FASE 5: Calendario tributario
            // ============================================================
            $this->addLog($this->getLang('process_cleaning_tax'));
            $db->query('DELETE FROM tax_calendar_files');
            $db->query('DELETE FROM tax_calendar_master');
            $db->query('DELETE FROM tax_calendar_status');
            $db->query('DELETE FROM tax_calendar_upload_log');
            $db->query('DELETE FROM tax_task_log');

            // ============================================================
            // FASE 6: Obligaciones de clientes
            // ============================================================
            $this->addLog($this->getLang('process_cleaning_clients'));
            $db->query('DELETE FROM client_obligations');
            $db->query('DELETE FROM client_custom_obligations');
            $db->query('DELETE FROM client_deadline_exceptions');

            // ============================================================
            // FASE 7: Reset secuencias y contadores
            // ============================================================
            $this->addLog($this->getLang('process_cleaning_sequences'));
            $db->query('UPDATE label SET count = 0');
            $db->query('UPDATE task_program SET count = 0');
            $db->query('UPDATE thread_sequence SET last_id = 0 WHERE id = 1');
            $db->query('UPDATE admin_thread_sequence SET last_id = 0 WHERE id = 1');
            $db->query('DELETE FROM sqlite_sequence');

            // 4. Limpiar archivos wiki de proyectos
            $this->addLog($this->getLang('process_cleaning_files'));
            $this->cleanupProjectFiles();

            // 5. Limpiar permisos ACL de proyectos
            $this->addLog($this->getLang('process_cleaning_acl'));
            $this->cleanupProjectACL();

            $this->message = $this->getLang('process_success');
            $this->message_type = 'success';

        } catch (Exception $e) {
            $this->message = $this->getLang('process_error') . ': ' . $e->getMessage();
            $this->message_type = 'error';
        }
    }

    /**
     * Crea una copia de seguridad antes de limpiar
     */
    protected function createBackup()
    {
        try {
            // Cargar el plugin de backup
            $backup_plugin = plugin_load('admin', 'backup');
            if (!$backup_plugin) {
                $this->addLog('Error: Plugin de backup no encontrado');
                return false;
            }

            // Crear nombre unico para el backup
            $tarfilename = 'dw-backup-cleanup-' . date('Ymd-His') . '.tar';
            if (extension_loaded('bz2')) {
                $tarfilename .= '.bz2';
            } elseif (extension_loaded('zlib')) {
                $tarfilename .= '.gz';
            }

            $ns = $backup_plugin->getConf('backupnamespace');
            $id = cleanID($ns . ':' . $tarfilename);
            $fn = mediaFN($id);

            // Configurar preferencias para backup completo
            $prefs = [
                'config' => 1,
                'pages' => 1,
                'revisions' => 0,
                'meta' => 1,
                'media' => 1,
                'mediarevs' => 0,
                'mediameta' => 0,
                'templates' => 0,
                'plugins' => 0
            ];

            // Usar reflection para acceder al metodo protegido
            $reflection = new ReflectionClass($backup_plugin);
            $method = $reflection->getMethod('createBackup');
            $method->setAccessible(true);

            // Logger simple que agrega al log
            $logger = function($msg, $level = 0) {
                // No hacer nada, solo continuar
            };

            $method->invoke($backup_plugin, $fn, $prefs, $logger);

            $this->addLog('Backup creado: ' . $tarfilename);
            return true;

        } catch (Exception $e) {
            $this->addLog('Error creando backup: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Limpia los archivos wiki de los proyectos
     */
    protected function cleanupProjectFiles()
    {
        global $conf;

        $baseDir = $conf['datadir'] . DIRECTORY_SEPARATOR . 'proyectos';

        // Directorios a limpiar completamente
        $cleanDirs = [
            $baseDir . DIRECTORY_SEPARATOR . 'generales',
            $baseDir . DIRECTORY_SEPARATOR . 'control',
            $baseDir . DIRECTORY_SEPARATOR . 'administrativos',
        ];

        // Limpiar directorios completamente
        foreach ($cleanDirs as $dir) {
            if (is_dir($dir)) {
                $this->deleteDirectoryContents($dir);
                $this->addLog('Limpiado: ' . $dir);
            }
        }

        // Directorios de auditorias (conservar _template2.txt)
        $auditoriasDir = $baseDir . DIRECTORY_SEPARATOR . 'auditorias' . DIRECTORY_SEPARATOR . 'programas_auditoria';

        if (is_dir($auditoriasDir)) {
            $this->cleanAuditoriasDir($auditoriasDir);
        }

        // Limpiar metadata de proyectos administrativos
        $metaAdminDir = $conf['metadir'] . DIRECTORY_SEPARATOR . 'proyectos' . DIRECTORY_SEPARATOR . 'administrativos';
        if (is_dir($metaAdminDir)) {
            $this->deleteDirectoryContents($metaAdminDir);
            $this->addLog('Limpiado meta: ' . $metaAdminDir);
        }
    }

    /**
     * Limpia los directorios de auditorias conservando _template2.txt
     */
    protected function cleanAuditoriasDir($dir)
    {
        if (!is_dir($dir)) {
            return;
        }

        $items = scandir($dir);
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }

            $path = $dir . DIRECTORY_SEPARATOR . $item;

            if (is_dir($path)) {
                // Para cada subdirectorio, limpiar contenido excepto _template2.txt
                $this->cleanDirectoryExceptTemplate($path);
                $this->addLog('Limpiado (conservando templates): ' . $path);
            }
        }
    }

    /**
     * Limpia un directorio conservando archivos _template2.txt
     */
    protected function cleanDirectoryExceptTemplate($dir)
    {
        if (!is_dir($dir)) {
            return;
        }

        $items = scandir($dir);
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }

            $path = $dir . DIRECTORY_SEPARATOR . $item;

            if (is_dir($path)) {
                // Es un subdirectorio de proyecto, eliminarlo completamente
                $this->deleteDirectoryRecursive($path);
            } elseif ($item !== '_template2.txt') {
                // Archivo que no es template, eliminar
                @unlink($path);
            }
        }
    }

    /**
     * Elimina el contenido de un directorio (pero no el directorio mismo)
     */
    protected function deleteDirectoryContents($dir)
    {
        if (!is_dir($dir)) {
            return;
        }

        $items = scandir($dir);
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }

            $path = $dir . DIRECTORY_SEPARATOR . $item;

            if (is_dir($path)) {
                $this->deleteDirectoryRecursive($path);
            } else {
                @unlink($path);
            }
        }
    }

    /**
     * Elimina un directorio recursivamente
     */
    protected function deleteDirectoryRecursive($dir)
    {
        if (!is_dir($dir)) {
            return true;
        }

        // Intentar con io_rmdir de DokuWiki
        if (function_exists('io_rmdir')) {
            if (@io_rmdir($dir, true)) {
                return true;
            }
        }

        // Fallback: eliminacion manual
        $items = scandir($dir);
        foreach ($items as $item) {
            if ($item === '.' || $item === '..') {
                continue;
            }

            $path = $dir . DIRECTORY_SEPARATOR . $item;

            if (is_dir($path)) {
                $this->deleteDirectoryRecursive($path);
            } else {
                @unlink($path);
            }
        }

        return @rmdir($dir);
    }

    /**
     * Limpia los permisos ACL de proyectos
     */
    protected function cleanupProjectACL()
    {
        $aclFile = DOKU_CONF . 'acl.auth.php';

        if (!file_exists($aclFile)) {
            return;
        }

        $lines = file($aclFile, FILE_IGNORE_NEW_LINES);
        $newLines = [];
        // Patrones correctos para ACL de proyectos (es:bez:thread:id:XXX y proyectos:)
        $projectPatterns = [
            '/^es:bez:thread:id:\d+/',  // Patrón principal de proyectos BEZ
            '/^proyectos:/'              // Patrón de carpetas de proyectos
        ];

        foreach ($lines as $line) {
            // Conservar lineas vacias, comentarios y permisos no relacionados con proyectos
            if (empty(trim($line)) || strpos(trim($line), '#') === 0) {
                $newLines[] = $line;
                continue;
            }

            // Verificar si la linea coincide con algun patron de proyecto
            $isProjectLine = false;
            foreach ($projectPatterns as $pattern) {
                if (preg_match($pattern, trim($line))) {
                    $isProjectLine = true;
                    break;
                }
            }

            // Si la linea NO es de un proyecto, conservarla
            if (!$isProjectLine) {
                $newLines[] = $line;
            }
        }

        // Guardar archivo limpio
        $content = implode("\n", $newLines);
        if (substr($content, -1) !== "\n") {
            $content .= "\n";
        }

        file_put_contents($aclFile, $content);
        $this->addLog('Permisos ACL de proyectos eliminados');
    }

    /**
     * Agrega un mensaje al log
     */
    protected function addLog($message)
    {
        $this->log[] = date('H:i:s') . ' - ' . $message;
    }

    /**
     * Genera la salida HTML
     */
    public function html()
    {
        global $INPUT;

        echo '<div id="dbcleanup_manager">';

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

        // Mostrar mensaje si hay
        if ($this->message) {
            $class = $this->message_type === 'success' ? 'notify' : 'error';
            echo '<div class="' . $class . '">';
            echo '<p>' . hsc($this->message) . '</p>';
            echo '</div>';
        }

        // Mostrar log si hay
        if (!empty($this->log)) {
            echo '<div class="log-output">';
            echo '<h3>Log de operaciones:</h3>';
            echo '<pre>';
            foreach ($this->log as $logLine) {
                echo hsc($logLine) . "\n";
            }
            echo '</pre>';
            echo '</div>';
        }

        // Determinar que pantalla mostrar
        $step = $INPUT->post->str('step', '');

        if ($step === 'confirm') {
            $this->showConfirmScreen();
        } else {
            $this->showWarningScreen();
        }

        $this->printStyles();
        echo '</div>';
    }

    /**
     * Muestra la pantalla de advertencia inicial
     */
    protected function showWarningScreen()
    {
        global $ID;

        echo '<h1>' . hsc($this->getLang('warning_title')) . '</h1>';

        echo '<div class="warning-box">';
        echo '<p class="warning-text">' . hsc($this->getLang('warning_message')) . '</p>';
        echo '<ul class="warning-list">';
        echo '<li>' . hsc($this->getLang('warning_item1')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item2')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item3')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item4')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item5')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item6')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item7')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item8')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item9')) . '</li>';
        echo '<li>' . hsc($this->getLang('warning_item10')) . '</li>';
        echo '</ul>';
        echo '<p class="warning-risk">' . hsc($this->getLang('warning_risk')) . '</p>';
        echo '</div>';

        echo '<form method="post" action="' . wl($ID, ['do' => 'admin', 'page' => 'dbcleanup']) . '">';
        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
        echo '<input type="hidden" name="step" value="confirm" />';

        echo '<div class="form-group">';
        echo '<label class="checkbox-label">';
        echo '<input type="checkbox" name="understand" id="understand" required />';
        echo ' ' . hsc($this->getLang('warning_confirm'));
        echo '</label>';
        echo '</div>';

        echo '<button type="submit" class="btn btn-danger" id="btn-continue">';
        echo hsc($this->getLang('btn_start'));
        echo '</button>';
        echo '</form>';

        // JavaScript para habilitar boton solo si checkbox esta marcado
        echo '<script>
            document.getElementById("understand").addEventListener("change", function() {
                document.getElementById("btn-continue").disabled = !this.checked;
            });
            document.getElementById("btn-continue").disabled = true;
        </script>';
    }

    /**
     * Muestra la pantalla de confirmacion final
     */
    protected function showConfirmScreen()
    {
        global $ID;

        echo '<h1>' . hsc($this->getLang('confirm_title')) . '</h1>';

        echo '<div class="confirm-box">';
        echo '<p class="confirm-text">' . hsc($this->getLang('confirm_message')) . '</p>';
        echo '</div>';

        echo '<form method="post" action="' . wl($ID, ['do' => 'admin', 'page' => 'dbcleanup']) . '">';
        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
        echo '<input type="hidden" name="confirm_cleanup" value="1" />';

        echo '<div class="form-group">';
        echo '<label for="confirm_password">' . hsc($this->getLang('confirm_password')) . '</label>';
        echo '<input type="password" name="confirm_password" id="confirm_password" class="form-control" required />';
        echo '</div>';

        echo '<div class="button-group">';
        echo '<button type="submit" class="btn btn-danger">';
        echo hsc($this->getLang('confirm_start'));
        echo '</button>';
        echo ' ';
        echo '<a href="' . wl($ID, ['do' => 'admin', 'page' => 'dbcleanup']) . '" class="btn btn-secondary">';
        echo hsc($this->getLang('confirm_cancel'));
        echo '</a>';
        echo '</div>';
        echo '</form>';
    }

    /**
     * Imprime los estilos CSS
     */
    protected function printStyles()
    {
        echo '<style>
            #dbcleanup_manager {
                max-width: 800px;
                margin: 0 auto;
                padding: 20px;
            }

            #dbcleanup_manager h1 {
                color: #c0392b;
                border-bottom: 2px solid #c0392b;
                padding-bottom: 10px;
            }

            .warning-box, .confirm-box {
                background: #fdf2f2;
                border: 2px solid #c0392b;
                border-radius: 8px;
                padding: 20px;
                margin: 20px 0;
            }

            .warning-text, .confirm-text {
                font-size: 1.1em;
                color: #721c24;
                margin-bottom: 15px;
            }

            .warning-list {
                background: white;
                padding: 15px 15px 15px 35px;
                border-radius: 5px;
                margin: 15px 0;
            }

            .warning-list li {
                padding: 5px 0;
                color: #721c24;
            }

            .warning-risk {
                background: #721c24;
                color: white;
                padding: 15px;
                border-radius: 5px;
                font-weight: bold;
                margin-top: 15px;
            }

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

            .checkbox-label {
                display: flex;
                align-items: center;
                gap: 10px;
                cursor: pointer;
                font-size: 1.05em;
            }

            .checkbox-label input[type="checkbox"] {
                width: 20px;
                height: 20px;
            }

            .form-control {
                display: block;
                width: 100%;
                max-width: 300px;
                padding: 10px;
                font-size: 1em;
                border: 1px solid #ccc;
                border-radius: 4px;
                margin-top: 5px;
            }

            .hint {
                display: block;
                color: #666;
                font-style: italic;
                margin-top: 5px;
            }

            .btn {
                display: inline-block;
                padding: 12px 24px;
                font-size: 1em;
                font-weight: bold;
                text-decoration: none;
                border: none;
                border-radius: 5px;
                cursor: pointer;
                transition: all 0.3s ease;
            }

            .btn-danger {
                background: #c0392b;
                color: white;
            }

            .btn-danger:hover {
                background: #a93226;
            }

            .btn-danger:disabled {
                background: #ccc;
                cursor: not-allowed;
            }

            .btn-secondary {
                background: #7f8c8d;
                color: white;
            }

            .btn-secondary:hover {
                background: #6c7a7b;
            }

            .button-group {
                margin-top: 20px;
            }

            .notify {
                background: #d4edda;
                border: 1px solid #c3e6cb;
                color: #155724;
                padding: 15px;
                border-radius: 5px;
                margin-bottom: 20px;
            }

            .error {
                background: #f8d7da;
                border: 1px solid #f5c6cb;
                color: #721c24;
                padding: 15px;
                border-radius: 5px;
                margin-bottom: 20px;
            }

            .log-output {
                background: #f8f9fa;
                border: 1px solid #dee2e6;
                border-radius: 5px;
                padding: 15px;
                margin: 20px 0;
            }

            .log-output h3 {
                margin-top: 0;
                color: #495057;
            }

            .log-output pre {
                background: #2d3436;
                color: #00b894;
                padding: 15px;
                border-radius: 5px;
                overflow-x: auto;
                font-family: monospace;
                font-size: 0.9em;
            }
        </style>';
    }
}
