<?php
/**
 * Comment Date Manager - Helper
 * Lógica de negocio para gestión de fechas de comentarios
 */

class helper_plugin_commentdates extends DokuWiki_Plugin
{
    /** @var helper_plugin_sqlite */
    protected $sqlite = null;

    /**
     * Obtiene conexión a la base de datos BEZ
     */
    protected function getDB()
    {
        if ($this->sqlite === null) {
            $this->sqlite = plugin_load('helper', 'sqlite');
            if (!$this->sqlite) {
                msg('El plugin sqlite es requerido', -1);
                return null;
            }
            if (!$this->sqlite->init('b3p', DOKU_PLUGIN . 'bez/db/')) {
                $this->sqlite = null;
                return null;
            }
        }
        return $this->sqlite;
    }

    /**
     * Obtiene lista de proyectos con información del cliente
     * @return array Lista de proyectos [id, title, create_date, cliente, tipo]
     */
    public function getProjects()
    {
        $db = $this->getDB();
        if (!$db) return [];

        $sql = "SELECT
                    t.id,
                    t.title,
                    t.create_date,
                    t.type,
                    t.state,
                    GROUP_CONCAT(l.name, ', ') as cliente
                FROM thread t
                LEFT JOIN thread_label tl ON t.id = tl.thread_id
                LEFT JOIN label l ON tl.label_id = l.id
                WHERE t.type IN ('issue', 'project')
                GROUP BY t.id
                ORDER BY t.id DESC
                LIMIT 500";

        $res = $db->query($sql);
        return $db->res2arr($res);
    }

    /**
     * Obtiene un proyecto específico por ID
     * @param int $projectId
     * @return array|null
     */
    public function getProject($projectId)
    {
        $db = $this->getDB();
        if (!$db) return null;

        $sql = "SELECT
                    t.id,
                    t.title,
                    t.create_date,
                    t.type,
                    t.state,
                    GROUP_CONCAT(l.name, ', ') as cliente
                FROM thread t
                LEFT JOIN thread_label tl ON t.id = tl.thread_id
                LEFT JOIN label l ON tl.label_id = l.id
                WHERE t.id = ?
                GROUP BY t.id";

        $res = $db->query($sql, $projectId);
        $rows = $db->res2arr($res);
        return !empty($rows) ? $rows[0] : null;
    }

    /**
     * Busca archivos .comments relacionados con un proyecto (búsqueda recursiva)
     * @param int $projectId
     * @return array Lista de archivos encontrados con su ruta completa
     */
    public function findProjectCommentFiles($projectId)
    {
        global $conf;
        $metaDir = $conf['metadir'];
        $files = [];

        // Carpetas base donde buscar recursivamente
        $baseDirs = [
            'proyectos/control/' . $projectId,
            'proyectos/generales/' . $projectId,
            'proyectos/auditorias/programas_auditoria/' . $projectId,
        ];

        foreach ($baseDirs as $baseDir) {
            $fullBaseDir = $metaDir . '/' . $baseDir;
            if (is_dir($fullBaseDir)) {
                // Buscar recursivamente todos los .comments
                $commentFiles = $this->findCommentsRecursive($fullBaseDir);
                foreach ($commentFiles as $fullPath) {
                    $data = $this->loadCommentsFile($fullPath);
                    $commentCount = isset($data['comments']) ? count($data['comments']) : 0;
                    // Calcular ruta relativa desde metadir
                    $relativePath = str_replace($metaDir . '/', '', $fullPath);
                    $relativePath = str_replace($metaDir . '\\', '', $relativePath);
                    $files[] = [
                        'path' => $fullPath,
                        'relative' => $relativePath,
                        'exists' => true,
                        'comment_count' => $commentCount
                    ];
                }
            }
        }

        return $files;
    }

    /**
     * Busca archivos .comments recursivamente en un directorio
     * @param string $dir Directorio base
     * @return array Lista de rutas completas a archivos .comments
     */
    protected function findCommentsRecursive($dir)
    {
        $results = [];

        if (!is_dir($dir)) {
            return $results;
        }

        $iterator = new RecursiveIteratorIterator(
            new RecursiveDirectoryIterator($dir, RecursiveDirectoryIterator::SKIP_DOTS),
            RecursiveIteratorIterator::SELF_FIRST
        );

        foreach ($iterator as $file) {
            if ($file->isFile() && $file->getExtension() === 'comments') {
                $results[] = $file->getPathname();
            }
        }

        return $results;
    }

    /**
     * Obtiene grupos administrativos disponibles (conteo recursivo)
     * @return array Lista de grupos con conteo de archivos
     */
    public function getAdminGroups()
    {
        global $conf;
        $metaDir = $conf['metadir'];
        $groups = [];

        // Buscar carpetas en varios/
        $variosPath = $metaDir . '/varios';
        if (is_dir($variosPath)) {
            $dirs = glob($variosPath . '/menu*', GLOB_ONLYDIR);
            foreach ($dirs as $dir) {
                $name = basename($dir);
                // Contar archivos .comments recursivamente
                $commentFiles = $this->findCommentsRecursive($dir);
                $groups[] = [
                    'name' => $name,
                    'path' => $dir,
                    'file_count' => count($commentFiles)
                ];
            }
        }

        return $groups;
    }

    /**
     * Busca archivos .comments del componente administrativo (búsqueda recursiva)
     * @param string $menuGroup Nombre del grupo (menu, menu1, menu2, etc.)
     * @return array Lista de archivos encontrados
     */
    public function findAdminCommentFiles($menuGroup)
    {
        global $conf;
        $metaDir = $conf['metadir'];
        $files = [];

        $groupPath = $metaDir . '/varios/' . $menuGroup;
        if (!is_dir($groupPath)) {
            return [];
        }

        // Buscar recursivamente todos los .comments
        $commentFiles = $this->findCommentsRecursive($groupPath);
        foreach ($commentFiles as $fullPath) {
            $data = $this->loadCommentsFile($fullPath);
            $commentCount = isset($data['comments']) ? count($data['comments']) : 0;
            // Calcular ruta relativa desde metadir
            $relativePath = str_replace($metaDir . '/', '', $fullPath);
            $relativePath = str_replace($metaDir . '\\', '', $relativePath);
            $files[] = [
                'path' => $fullPath,
                'relative' => $relativePath,
                'exists' => true,
                'comment_count' => $commentCount
            ];
        }

        return $files;
    }

    /**
     * Carga y deserializa un archivo .comments
     * @param string $filePath
     * @return array
     */
    public function loadCommentsFile($filePath)
    {
        if (!file_exists($filePath)) {
            return ['comments' => []];
        }

        $content = io_readFile($filePath, false);
        if (empty($content)) {
            return ['comments' => []];
        }

        $data = @unserialize($content, ['allowed_classes' => false]);
        if ($data === false) {
            return ['comments' => []];
        }

        return $data;
    }

    /**
     * Guarda un archivo .comments serializado
     * @param string $filePath
     * @param array $data
     * @return bool
     */
    public function saveCommentsFile($filePath, $data)
    {
        return io_saveFile($filePath, serialize($data));
    }

    /**
     * Crea backup de un archivo
     * @param string $filePath
     * @return bool
     */
    public function backupFile($filePath)
    {
        if (!file_exists($filePath)) {
            return false;
        }

        $backupPath = $filePath . '.bak.' . date('YmdHis');
        return copy($filePath, $backupPath);
    }

    /**
     * Convierte fecha a timestamp
     * @param string $dateStr Fecha en formato Y-m-d H:i o Y-m-d H:i:s
     * @return int
     */
    public function dateToTimestamp($dateStr)
    {
        // Asegurar formato con segundos
        if (strlen($dateStr) === 16) {
            $dateStr .= ':00';
        }
        return strtotime($dateStr);
    }

    /**
     * Actualiza las fechas de comentarios en múltiples archivos
     * @param array $files Lista de archivos con path
     * @param int $newTimestamp
     * @return array Resultado con éxitos y errores
     */
    public function updateMultipleCommentDates($files, $newTimestamp)
    {
        $result = [
            'success' => true,
            'success_count' => 0,
            'error_count' => 0,
            'total_changes' => 0,
            'details' => []
        ];

        foreach ($files as $file) {
            $data = $this->loadCommentsFile($file['path']);
            $changes = 0;

            if (isset($data['comments']) && is_array($data['comments'])) {
                foreach ($data['comments'] as $cid => &$comment) {
                    if (isset($comment['date']['created'])) {
                        $comment['date']['created'] = $newTimestamp;
                        $changes++;
                    }
                }
            }

            if ($changes > 0) {
                $saved = $this->saveCommentsFile($file['path'], $data);
                if ($saved) {
                    $result['success_count']++;
                    $result['total_changes'] += $changes;
                    $result['details'][] = [
                        'file' => $file['relative'],
                        'success' => true,
                        'changes' => $changes
                    ];
                } else {
                    $result['error_count']++;
                    $result['success'] = false;
                    $result['details'][] = [
                        'file' => $file['relative'],
                        'success' => false,
                        'changes' => 0
                    ];
                }
            } else {
                $result['details'][] = [
                    'file' => $file['relative'],
                    'success' => true,
                    'changes' => 0
                ];
            }
        }

        return $result;
    }

    /**
     * Actualiza la fecha de creación de un proyecto en BEZ
     * @param int $projectId
     * @param string $newDate Fecha en formato Y-m-d H:i:s
     * @return array
     */
    public function updateBezProjectDate($projectId, $newDate)
    {
        $db = $this->getDB();
        if (!$db) {
            return ['success' => false, 'message' => 'No se pudo conectar a la base de datos'];
        }

        // Obtener fecha actual para el mensaje
        $current = $this->getProject($projectId);
        if (!$current) {
            return ['success' => false, 'message' => 'Proyecto no encontrado'];
        }

        // Convertir a formato ISO 8601 con T (consistente con el resto de BEZ)
        $isoDate = date('c', strtotime($newDate));

        $sql = "UPDATE thread SET create_date = ? WHERE id = ?";
        $res = $db->query($sql, $isoDate, $projectId);

        if ($res !== false) {
            return [
                'success' => true,
                'message' => "Fecha actualizada de '{$current['create_date']}' a '$isoDate'"
            ];
        }

        return ['success' => false, 'message' => 'Error al actualizar la base de datos'];
    }
}
