<?php
/**
 * DokuWiki Plugin docmaster (Action Component)
 *
 * Hooks:
 *  - COMMON_WIKIPAGE_SAVE (AFTER)  → auto-parseo al guardar páginas
 *  - AJAX_CALL_UNKNOWN (BEFORE)    → endpoints para edición inline, escaneo, listados
 *
 * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
 * @author  SIE Systems <soporte@systemsie.site>
 */

class action_plugin_docmaster extends DokuWiki_Action_Plugin
{
    /**
     * Registrar eventos
     */
    public function register(Doku_Event_Handler $controller)
    {
        $controller->register_hook('COMMON_WIKIPAGE_SAVE', 'AFTER', $this, 'handle_save');
        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax');
    }

    // =========================================================================
    //  HOOK: COMMON_WIKIPAGE_SAVE
    // =========================================================================

    /**
     * Al guardar una página, parsear su contenido y actualizar la DB.
     *
     * Si el usuario marcó "Cambios mayores" (que en DokuWiki español
     * corresponde a changeType == DOKU_CHANGE_TYPE_MINOR_EDIT cuando
     * la etiqueta dice "Cambios mayores"), se incrementa la versión.
     *
     * NOTA IMPORTANTE sobre "Cambios mayores":
     * En DokuWiki, el checkbox se llama "minor" y si está marcado,
     * changeType = DOKU_CHANGE_TYPE_MINOR_EDIT.
     * En la versión en español del sistema SIE, la etiqueta dice
     * "Cambios mayores" y su significado está INVERTIDO:
     *   - Checkbox marcado = Cambios mayores → minor=true → auto-incrementar versión
     *   - Checkbox no marcado = Cambios menores → minor=false → no incrementar
     */
    public function handle_save(Doku_Event $event, $param)
    {
        // Solo procesar si hubo cambio real de contenido
        if (!$event->data['contentChanged']) {
            return;
        }

        /** @var helper_plugin_docmaster $helper */
        $helper = plugin_load('helper', 'docmaster');
        if (!$helper) return;

        $pageId = $event->data['id'];

        // Verificar namespace calificado
        if (!$helper->isQualifiedNamespace($pageId)) {
            return;
        }

        $content = $event->data['newContent'];
        $user = $event->data['user'] ?? '';

        // Procesar página (parsear encabezado + formatos → upsert en DB)
        $result = $helper->processPage($pageId, $content, $user);

        if (!$result) return;

        // --- Auto-versionado: "Cambios mayores" ---
        // En DokuWiki español SIE, "Cambios mayores" = checkbox minor marcado
        $changeType = $event->data['changeType'];

        if ($changeType === DOKU_CHANGE_TYPE_MINOR_EDIT) {
            $docId = $result['doc_id'];
            $newDate = date('d/m/Y');

            // Incrementar versión en DB
            $newVersion = $helper->incrementVersion($docId, $user, $newDate);

            if ($newVersion > 0) {
                // Actualizar contenido de la página con nueva versión y vigencia
                $updatedContent = $helper->updatePageContent($content, $newVersion, $newDate);

                // Solo re-guardar si el contenido cambió
                if ($updatedContent !== $content) {
                    // Guardar la página con el contenido actualizado
                    // Usamos io_saveFile directamente para evitar recursión
                    $file = wikiFN($pageId);
                    io_saveFile($file, $updatedContent);
                }
            }
        }
    }

    // =========================================================================
    //  HOOK: AJAX_CALL_UNKNOWN
    // =========================================================================

    /**
     * Maneja llamadas AJAX del plugin
     */
    public function handle_ajax(Doku_Event $event, $param)
    {
        $call = $event->data;

        // Solo responder a nuestros endpoints
        if (strpos($call, 'plugin_docmaster_') !== 0) {
            return;
        }

        $event->stopPropagation();
        $event->preventDefault();

        // Verificar autenticación (admins y managers)
        global $INPUT;
        if (!auth_ismanager()) {
            $this->_jsonResponse(['error' => 'No autorizado'], 403);
            return;
        }

        /** @var helper_plugin_docmaster $helper */
        $helper = plugin_load('helper', 'docmaster');
        if (!$helper || !$helper->getDB()) {
            $this->_jsonResponse(['error' => 'Error de base de datos'], 500);
            return;
        }

        switch ($call) {
            case 'plugin_docmaster_list':
                $this->_ajaxList($helper);
                break;

            case 'plugin_docmaster_formats':
                $this->_ajaxFormats($helper);
                break;

            case 'plugin_docmaster_update':
                $this->_ajaxUpdate($helper);
                break;

            case 'plugin_docmaster_update_format':
                $this->_ajaxUpdateFormat($helper);
                break;

            case 'plugin_docmaster_scan':
                $this->_ajaxScan($helper);
                break;

            case 'plugin_docmaster_doc_formats':
                $this->_ajaxDocFormats($helper);
                break;

            case 'plugin_docmaster_stats':
                $this->_ajaxStats($helper);
                break;

            case 'plugin_docmaster_version_history':
                $this->_ajaxVersionHistory($helper);
                break;

            case 'plugin_docmaster_duplicates':
                $this->_ajaxDuplicates($helper);
                break;

            case 'plugin_docmaster_fix_duplicate':
                $this->_ajaxFixDuplicate($helper);
                break;

            case 'plugin_docmaster_fix_all_duplicates':
                $this->_ajaxFixAllDuplicates($helper);
                break;

            case 'plugin_docmaster_sync_format':
                $this->_ajaxSyncFormat($helper);
                break;

            case 'plugin_docmaster_bulk_update':
                $this->_ajaxBulkUpdate($helper);
                break;

            case 'plugin_docmaster_bulk_update_format':
                $this->_ajaxBulkUpdateFormat($helper);
                break;

            case 'plugin_docmaster_repair_all_formats':
                $this->_ajaxRepairAllFormats($helper);
                break;

            default:
                $this->_jsonResponse(['error' => 'Endpoint desconocido'], 404);
        }
    }

    /**
     * AJAX: Listar documentos
     */
    protected function _ajaxList($helper)
    {
        global $INPUT;

        $filters = [
            'namespace'   => $INPUT->str('namespace', ''),
            'search'      => $INPUT->str('search', ''),
            'active_only' => $INPUT->bool('active_only', true),
        ];

        $documents = $helper->getDocuments($filters);
        $namespaces = $helper->getNamespaces();

        $this->_jsonResponse([
            'documents'  => $documents,
            'namespaces' => $namespaces,
            'total'      => count($documents),
        ]);
    }

    /**
     * AJAX: Listar formatos
     */
    protected function _ajaxFormats($helper)
    {
        global $INPUT;

        $filters = [
            'search' => $INPUT->str('search', ''),
        ];

        $formats = $helper->getFormats($filters);

        $this->_jsonResponse([
            'formats' => $formats,
            'total'   => count($formats),
        ]);
    }

    /**
     * AJAX: Actualizar campo de documento
     */
    protected function _ajaxUpdate($helper)
    {
        global $INPUT;

        $docId = $INPUT->int('id');
        $field = $INPUT->str('field');
        $value = $INPUT->str('value');

        if (!$docId || !$field) {
            $this->_jsonResponse(['error' => 'Parámetros faltantes'], 400);
            return;
        }

        $success = $helper->updateDocumentField($docId, $field, $value);

        $this->_jsonResponse([
            'success' => $success,
            'id'      => $docId,
            'field'   => $field,
            'value'   => $value,
        ]);
    }

    /**
     * AJAX: Actualizar campo de formato
     */
    protected function _ajaxUpdateFormat($helper)
    {
        global $INPUT;

        $fmtId = $INPUT->int('id');
        $field = $INPUT->str('field');
        $value = $INPUT->str('value');

        if (!$fmtId || !$field) {
            $this->_jsonResponse(['error' => 'Parámetros faltantes'], 400);
            return;
        }

        $success = $helper->updateFormatField($fmtId, $field, $value);

        $this->_jsonResponse([
            'success' => $success,
            'id'      => $fmtId,
            'field'   => $field,
            'value'   => $value,
        ]);
    }

    /**
     * AJAX: Escanear todas las páginas
     * Versión de diagnóstico: v2 (2026-03-05)
     */
    protected function _ajaxScan($helper)
    {
        // Aumentar tiempo límite para escaneo masivo
        @set_time_limit(300);

        // Limpiar cualquier output previo que pueda corromper JSON
        ob_start();

        try {
            $stats = $helper->scanAllPages();
        } catch (\Exception $e) {
            ob_end_clean();
            $this->_jsonResponse([
                'success' => false,
                'error'   => 'Exception: ' . $e->getMessage(),
                '_v'      => 2,
            ]);
            return;
        } catch (\Error $e) {
            ob_end_clean();
            $this->_jsonResponse([
                'success' => false,
                'error'   => 'Error fatal: ' . $e->getMessage() . ' en ' . $e->getFile() . ':' . $e->getLine(),
                '_v'      => 2,
            ]);
            return;
        }

        // Capturar warnings/notices que pudieron salir
        $strayOutput = ob_get_clean();

        $this->_jsonResponse([
            'success'       => true,
            'processed'     => $stats['processed'] ?? 0,
            'skipped'       => $stats['skipped'] ?? 0,
            'errors'        => $stats['errors'] ?? 0,
            'removed'       => $stats['removed'] ?? 0,
            'files_found'   => $stats['files_found'] ?? 0,
            'dirs_found'    => $stats['dirs_found'] ?? 0,
            'dirs_checked'  => $stats['dirs_checked'] ?? 0,
            'skipped_pages' => $stats['skipped_pages'] ?? [],
            'datadir'       => $stats['datadir'] ?? '',
            'debug'         => !empty($strayOutput) ? substr($strayOutput, 0, 500) : null,
            '_v'            => 2,
        ]);
    }

    /**
     * AJAX: Obtener formatos de un documento
     */
    protected function _ajaxDocFormats($helper)
    {
        global $INPUT;

        $docId = $INPUT->int('doc_id');
        if (!$docId) {
            $this->_jsonResponse(['error' => 'doc_id requerido'], 400);
            return;
        }

        $formats = $helper->getDocumentFormats($docId);

        $this->_jsonResponse([
            'formats' => $formats,
            'total'   => count($formats),
        ]);
    }

    /**
     * AJAX: Estadísticas
     */
    protected function _ajaxStats($helper)
    {
        $stats = $helper->getStats();
        $this->_jsonResponse($stats);
    }

    /**
     * AJAX: Historial de versiones de un documento
     */
    protected function _ajaxVersionHistory($helper)
    {
        global $INPUT;

        $docId = $INPUT->int('doc_id');
        if (!$docId) {
            $this->_jsonResponse(['error' => 'doc_id requerido'], 400);
            return;
        }

        $history = $helper->getVersionHistory($docId);

        $this->_jsonResponse([
            'history' => $history,
            'total'   => count($history),
        ]);
    }

    // =========================================================================
    //  CÓDIGOS DUPLICADOS
    // =========================================================================

    /**
     * AJAX: Obtener códigos duplicados de documentos y formatos
     */
    protected function _ajaxDuplicates($helper)
    {
        $docDups = $helper->getDuplicateCodes('documents');
        $fmtDups = $helper->getDuplicateCodes('formats');

        $this->_jsonResponse([
            'documents' => $docDups,
            'formats'   => $fmtDups,
        ]);
    }

    /**
     * AJAX: Corregir un código duplicado asignando el siguiente consecutivo
     */
    protected function _ajaxFixDuplicate($helper)
    {
        global $INPUT;

        $id   = $INPUT->int('id');
        $type = $INPUT->str('type', 'document');

        if (!$id) {
            $this->_jsonResponse(['error' => 'Parámetros faltantes'], 400);
            return;
        }

        // Normalizar type
        $type = ($type === 'format' || $type === 'formats') ? 'formats' : 'documents';

        $result = $helper->fixDuplicateCode($id, $type);

        $this->_jsonResponse($result);
    }

    /**
     * AJAX: Sincronizar datos de un formato a las páginas wiki donde está referenciado
     * Actualiza el display_name (code, version, validity) en los links
     */
    protected function _ajaxSyncFormat($helper)
    {
        global $INPUT;

        $fmtId = $INPUT->int('id');
        if (!$fmtId) {
            $this->_jsonResponse(['error' => 'Parámetros faltantes'], 400);
            return;
        }

        $result = $helper->syncFormatToWikiPages($fmtId);

        $this->_jsonResponse([
            'success'       => true,
            'updated_pages' => $result['updated_pages'],
            'errors'        => $result['errors'],
            'debug'         => $result['debug'] ?? [],
        ]);
    }

    /**
     * AJAX: Corregir códigos duplicados (todos o solo los seleccionados)
     */
    protected function _ajaxFixAllDuplicates($helper)
    {
        global $INPUT;
        @set_time_limit(120);

        // Filtro opcional: solo corregir IDs seleccionados
        $docIdsJson = $INPUT->str('doc_ids', '');
        $fmtIdsJson = $INPUT->str('fmt_ids', '');
        $docIds = $docIdsJson ? json_decode($docIdsJson, true) : null;
        $fmtIds = $fmtIdsJson ? json_decode($fmtIdsJson, true) : null;

        // Si se enviaron arrays vacíos, tratarlos como null (sin filtro)
        if (is_array($docIds) && empty($docIds)) $docIds = null;
        if (is_array($fmtIds) && empty($fmtIds)) $fmtIds = null;

        $filter = null;
        if ($docIds !== null || $fmtIds !== null) {
            $filter = [
                'documents' => $docIds ?: [],
                'formats'   => $fmtIds ?: [],
            ];
        }

        $result = $helper->fixAllDuplicates($filter);

        $this->_jsonResponse([
            'success'     => true,
            'corrections' => $result['corrections'],
            'errors'      => $result['errors'],
            'total_fixed' => count($result['corrections']),
            'total_errors' => count($result['errors']),
        ]);
    }

    /**
     * AJAX: Reparador General - sincronizar todos (o seleccionados) formatos
     * con sus páginas wiki, corrigiendo los textos de hipervínculos.
     */
    protected function _ajaxRepairAllFormats($helper)
    {
        global $INPUT;
        @set_time_limit(300);

        // Filtro opcional: solo reparar IDs seleccionados
        $fmtIdsJson = $INPUT->str('fmt_ids', '');
        $fmtIds = $fmtIdsJson ? json_decode($fmtIdsJson, true) : null;

        // Array vacío = sin filtro (reparar todos)
        if (is_array($fmtIds) && empty($fmtIds)) {
            $fmtIds = null;
        }

        $result = $helper->repairAllFormats($fmtIds);

        $this->_jsonResponse([
            'success'             => true,
            'repaired'            => $result['repaired'],
            'skipped'             => $result['skipped'],
            'errors'              => $result['errors'],
            'total_formats'       => $result['total_formats'],
            'total_pages_updated' => $result['total_pages_updated'],
            'total_repaired'      => count($result['repaired']),
            'total_skipped'       => count($result['skipped']),
            'total_errors'        => count($result['errors']),
        ]);
    }

    // =========================================================================
    //  EDICIÓN MASIVA (BULK UPDATE)
    // =========================================================================

    /**
     * AJAX: Actualización masiva de campo en múltiples documentos.
     * Actualiza la BD Y la página wiki de cada documento.
     */
    protected function _ajaxBulkUpdate($helper)
    {
        global $INPUT;

        $idsJson = $INPUT->str('ids');
        $field   = $INPUT->str('field');
        $value   = $INPUT->str('value');

        $ids = json_decode($idsJson, true);
        if (!is_array($ids) || empty($ids) || !$field) {
            $this->_jsonResponse(['error' => 'Parámetros faltantes'], 400);
            return;
        }

        @set_time_limit(120);

        $updated = 0;
        $wikiUpdated = 0;
        $errors  = 0;

        foreach ($ids as $id) {
            $id = (int) $id;
            if ($id <= 0) { $errors++; continue; }

            // 1. Obtener valor anterior y page_id del documento
            $doc = $helper->getDocument($id);
            if (!$doc) { $errors++; continue; }

            $oldValue = isset($doc[$field]) ? $doc[$field] : '';
            $pageId   = isset($doc['page_id']) ? $doc['page_id'] : '';

            // 2. Actualizar la base de datos
            $result = $helper->updateDocumentField($id, $field, $value);
            if (!$result) { $errors++; continue; }
            $updated++;

            // 3. Actualizar la página wiki del documento
            if (!empty($pageId) && $oldValue != $value) {
                $wikiOk = $helper->updateFieldInWikiPage($pageId, $field, (string) $oldValue, (string) $value);
                if ($wikiOk) $wikiUpdated++;
            }
        }

        $this->_jsonResponse([
            'success'      => true,
            'updated'      => $updated,
            'wiki_updated' => $wikiUpdated,
            'errors'       => $errors,
            'total'        => count($ids),
            'field'        => $field,
            'value'        => $value,
        ]);
    }

    /**
     * AJAX: Actualización masiva de campo en múltiples formatos.
     * Actualiza la BD Y sincroniza cada formato con las páginas wiki.
     */
    protected function _ajaxBulkUpdateFormat($helper)
    {
        global $INPUT;

        $idsJson = $INPUT->str('ids');
        $field   = $INPUT->str('field');
        $value   = $INPUT->str('value');

        $ids = json_decode($idsJson, true);
        if (!is_array($ids) || empty($ids) || !$field) {
            $this->_jsonResponse(['error' => 'Parámetros faltantes'], 400);
            return;
        }

        @set_time_limit(120);

        $updated = 0;
        $wikiUpdated = 0;
        $errors  = 0;

        foreach ($ids as $id) {
            $id = (int) $id;
            if ($id <= 0) { $errors++; continue; }

            // 1. Actualizar la base de datos
            $result = $helper->updateFormatField($id, $field, $value);
            if (!$result) { $errors++; continue; }
            $updated++;

            // 2. Sincronizar formato con las páginas wiki donde está referenciado
            $syncResult = $helper->syncFormatToWikiPages($id);
            if ($syncResult && $syncResult['updated_pages'] > 0) {
                $wikiUpdated++;
            }
        }

        $this->_jsonResponse([
            'success'      => true,
            'updated'      => $updated,
            'wiki_updated' => $wikiUpdated,
            'errors'       => $errors,
            'total'        => count($ids),
            'field'        => $field,
            'value'        => $value,
        ]);
    }

    // =========================================================================
    //  UTILIDADES
    // =========================================================================

    /**
     * Envía una respuesta JSON
     *
     * @param array $data
     * @param int   $code HTTP status code
     */
    protected function _jsonResponse($data, $code = 200)
    {
        if ($code !== 200) {
            http_response_code($code);
        }

        header('Content-Type: application/json');
        echo json_encode($data, JSON_UNESCAPED_UNICODE);
    }
}
