<?php
/**
 * Admin plugin for Manager Group Protection
 *
 * Permite ver el estado de la proteccion del grupo manager
 * y sincronizar usuarios existentes
 */

use dokuwiki\Extension\AdminPlugin;

class admin_plugin_managerprotect extends AdminPlugin
{
    protected $message = '';
    protected $message_type = '';

    /** Nombre del archivo que almacena el hash de la contrasena SIE */
    const SIE_HASH_FILE = 'managerprotect.sie.hash';

    /**
     * Ruta completa al archivo de hash
     */
    public static function getSieHashPath()
    {
        return DOKU_CONF . self::SIE_HASH_FILE;
    }

    /**
     * Obtiene el hash almacenado, o null si no existe
     */
    public static function getSieHash()
    {
        $path = self::getSieHashPath();
        if (!file_exists($path)) return null;
        $hash = trim(file_get_contents($path));
        return ($hash !== '') ? $hash : null;
    }

    /**
     * Guarda una nueva contrasena SIE (como hash bcrypt)
     */
    public static function saveSieHash($plainPassword)
    {
        $hash = password_hash($plainPassword, PASSWORD_BCRYPT);
        return file_put_contents(self::getSieHashPath(), $hash) !== false;
    }

    /**
     * Verifica una contrasena contra el hash almacenado
     */
    public static function verifySiePassword($plainPassword)
    {
        $hash = self::getSieHash();
        if ($hash === null) return false;
        return password_verify($plainPassword, $hash);
    }

    /**
     * Accesible para managers y directores
     */
    public function forAdminOnly()
    {
        return false;
    }

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

        // 1. Verificar permisos nativos de DokuWiki
        if (auth_isadmin()) {
            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;
        }

        return false;
    }

    /**
     * Orden en el menu
     */
    public function getMenuSort()
    {
        return 200;
    }

    /**
     * Texto del menu
     */
    public function getMenuText($language)
    {
        return 'Proteccion Grupo Manager';
    }

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

    /**
     * Manejar acciones
     */
    public function handle()
    {
        global $INPUT;

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

        if ($INPUT->post->has('sync_users') && checkSecurityToken()) {
            $this->syncAllUsers();
        }

        // Manejar configuracion de contrasena SIE
        if ($INPUT->post->has('sie_password_action') && checkSecurityToken()) {
            $sieAction = $INPUT->post->str('sie_password_action');

            if ($sieAction === 'set') {
                $newPass = $INPUT->post->str('sie_new_password');
                $confirmPass = $INPUT->post->str('sie_confirm_password');

                if (empty($newPass)) {
                    $this->message = 'La contrasena SIE no puede estar vacia.';
                    $this->message_type = 'error';
                } elseif (strlen($newPass) < 8) {
                    $this->message = 'La contrasena debe tener al menos 8 caracteres.';
                    $this->message_type = 'error';
                } elseif ($newPass !== $confirmPass) {
                    $this->message = 'Las contrasenas no coinciden.';
                    $this->message_type = 'error';
                } else {
                    // Si ya existe contrasena, verificar la actual antes de cambiar
                    if (self::getSieHash() !== null) {
                        $currentPass = $INPUT->post->str('sie_current_password');
                        if (!password_verify($currentPass, self::getSieHash())) {
                            $this->message = 'La contrasena actual es incorrecta.';
                            $this->message_type = 'error';
                            return;
                        }
                    }

                    if (self::saveSieHash($newPass)) {
                        $this->message = 'Contrasena SIE configurada correctamente.';
                        $this->message_type = 'success';
                    } else {
                        $this->message = 'Error al guardar. Verifique permisos de escritura en conf/.';
                        $this->message_type = 'error';
                    }
                }
            } elseif ($sieAction === 'remove') {
                $currentPass = $INPUT->post->str('sie_current_password');
                if (self::verifySiePassword($currentPass)) {
                    $path = self::getSieHashPath();
                    if (file_exists($path) && @unlink($path)) {
                        $this->message = 'Contrasena SIE eliminada. La creacion de usuarios SIE esta BLOQUEADA hasta configurar una nueva.';
                        $this->message_type = 'success';
                    } else {
                        $this->message = 'Error al eliminar la contrasena.';
                        $this->message_type = 'error';
                    }
                } else {
                    $this->message = 'La contrasena actual es incorrecta.';
                    $this->message_type = 'error';
                }
            }
        }
    }

    /**
     * Sincronizar todos los usuarios
     */
    protected function syncAllUsers()
    {
        global $auth;

        if (!$auth || !method_exists($auth, 'retrieveUsers')) {
            $this->message = 'El backend de autenticacion no soporta listar usuarios.';
            $this->message_type = 'error';
            return;
        }

        $users = $auth->retrieveUsers(0, 0); // Obtener todos
        $updated = 0;
        $errors = 0;

        foreach ($users as $username => $userinfo) {
            $currentGroups = isset($userinfo['grps']) ? $userinfo['grps'] : [];
            $newGroups = $this->enforceManagerRule($username, $currentGroups);

            if ($currentGroups != $newGroups) {
                $result = $auth->modifyUser($username, ['grps' => $newGroups]);
                if ($result) {
                    $updated++;
                } else {
                    $errors++;
                }
            }
        }

        if ($errors > 0) {
            $this->message = "Sincronizacion completada: {$updated} usuarios actualizados, {$errors} errores.";
            $this->message_type = 'error';
        } else {
            $this->message = "Sincronizacion completada: {$updated} usuarios actualizados.";
            $this->message_type = 'success';
        }
    }

    /**
     * Aplica las reglas de grupos
     *
     * Reglas:
     * - Grupo 'manager': SOLO usuarios "sie*" pueden tenerlo. Se auto-quita a los demas.
     * - Grupo 'sie': Se auto-asigna a "sie*", pero otros usuarios tambien pueden tenerlo.
     */
    protected function enforceManagerRule($username, $groups)
    {
        $protectedGroup = 'manager';
        $sieGroup = 'sie';
        $requiredPrefix = 'sie';

        if (!is_array($groups)) {
            $groups = [];
        }

        $usernameStartsWithSie = (stripos($username, $requiredPrefix) === 0);

        if ($usernameStartsWithSie) {
            // Auto-asignar grupo 'manager' y grupo 'sie' a usuarios sie*
            if (!in_array($protectedGroup, $groups)) {
                $groups[] = $protectedGroup;
            }
            if (!in_array($sieGroup, $groups)) {
                $groups[] = $sieGroup;
            }
        } else {
            // Quitar SOLO grupo 'manager' a usuarios que NO son sie*
            // El grupo 'sie' NO se toca (otros usuarios pueden tenerlo)
            if (in_array($protectedGroup, $groups)) {
                $groups = array_filter($groups, function($g) use ($protectedGroup) {
                    return $g !== $protectedGroup;
                });
                $groups = array_values($groups);
            }
        }

        return $groups;
    }

    /**
     * Renderizar HTML
     */
    public function html()
    {
        global $auth, $ID;

        if (!$this->hasAccess()) {
            echo '<div class="error"><p>Acceso denegado. Se requiere nivel de administrador.</p></div>';
            return;
        }

        echo '<h1>Proteccion del Grupo Manager</h1>';

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

        // Explicacion
        echo '<div class="level1">';
        echo '<p>Este modulo protege el grupo <strong>manager</strong> con las siguientes reglas:</p>';
        echo '<ul>';
        echo '<li>Solo usuarios con nombre que inicie con <strong>"sie"</strong> (mayusculas o minusculas) pueden tener el grupo <strong>manager</strong></li>';
        echo '<li>El grupo "manager" se asigna automaticamente a usuarios "sie*" al crear o modificar</li>';
        echo '<li>El grupo "manager" se remueve automaticamente de usuarios que no inicien con "sie"</li>';
        echo '<li>El grupo "manager" esta <strong>oculto</strong> en el formulario de administracion de usuarios</li>';
        echo '<li>El grupo "sie" se auto-asigna a usuarios "sie*", pero <strong>otros usuarios tambien pueden tenerlo</strong></li>';
        echo '<li>El grupo "sie" es <strong>visible</strong> en la interfaz de administracion</li>';
        echo '</ul>';
        echo '</div>';

        // === SECCION: Contrasena de Creacion SIE ===
        echo '<h2>Contrasena de Creacion SIE</h2>';
        echo '<div class="level2">';

        $hasPassword = (self::getSieHash() !== null);

        if ($hasPassword) {
            echo '<p style="color: green;">&#10004; La contrasena SIE esta <strong>configurada</strong>. ';
            echo 'Se requiere para crear usuarios con nombre que inicie con "sie".</p>';
        } else {
            echo '<p style="color: red;">&#10008; <strong>No hay contrasena SIE configurada.</strong> ';
            echo 'La creacion de usuarios "sie*" esta BLOQUEADA hasta configurar una contrasena.</p>';
        }

        echo '<form method="post" action="' . wl($ID, ['do' => 'admin', 'page' => 'managerprotect']) . '">';
        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
        echo '<input type="hidden" name="sie_password_action" value="set" />';
        echo '<table class="inline" style="max-width: 500px;">';

        if ($hasPassword) {
            echo '<tr>';
            echo '<td><label for="sie_current_password">Contrasena actual:</label></td>';
            echo '<td><input type="password" id="sie_current_password" name="sie_current_password" autocomplete="off" /></td>';
            echo '</tr>';
        }

        echo '<tr>';
        echo '<td><label for="sie_new_password">Nueva contrasena:</label></td>';
        echo '<td><input type="password" id="sie_new_password" name="sie_new_password" autocomplete="off" required minlength="8" /></td>';
        echo '</tr>';

        echo '<tr>';
        echo '<td><label for="sie_confirm_password">Confirmar contrasena:</label></td>';
        echo '<td><input type="password" id="sie_confirm_password" name="sie_confirm_password" autocomplete="off" required minlength="8" /></td>';
        echo '</tr>';

        echo '<tr><td colspan="2">';
        echo '<button type="submit" class="button">' . ($hasPassword ? 'Cambiar Contrasena SIE' : 'Configurar Contrasena SIE') . '</button>';
        echo '</td></tr>';
        echo '</table>';
        echo '</form>';

        if ($hasPassword) {
            echo '<details style="margin-top: 10px;">';
            echo '<summary style="cursor:pointer; color:#c0392b;">Eliminar contrasena SIE...</summary>';
            echo '<form method="post" action="' . wl($ID, ['do' => 'admin', 'page' => 'managerprotect']) . '" style="margin-top:8px;">';
            echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
            echo '<input type="hidden" name="sie_password_action" value="remove" />';
            echo '<label>Contrasena actual: <input type="password" name="sie_current_password" autocomplete="off" required /></label> ';
            echo '<button type="submit" class="button" style="background:#c0392b;" onclick="return confirm(\'Eliminar la contrasena bloqueara la creacion de usuarios SIE. Continuar?\')">Eliminar</button>';
            echo '</form>';
            echo '</details>';
        }

        echo '</div>';

        // Tabla de usuarios
        echo '<h2>Estado Actual de Usuarios</h2>';

        if (!$auth || !method_exists($auth, 'retrieveUsers')) {
            echo '<div class="error"><p>El backend de autenticacion no soporta listar usuarios.</p></div>';
            return;
        }

        $users = $auth->retrieveUsers(0, 0);

        // Paginacion para evitar renderizar cientos de filas de golpe
        $perPage = 50;
        $totalUsers = count($users);
        $totalPages = max(1, (int)ceil($totalUsers / $perPage));
        $currentPage = max(1, min($totalPages, (int)($_REQUEST['pg'] ?? 1)));
        $usersSlice = array_slice($users, ($currentPage - 1) * $perPage, $perPage, true);

        echo '<p>Total de usuarios: <strong>' . $totalUsers . '</strong>';
        if ($totalPages > 1) {
            echo ' | Pagina ' . $currentPage . ' de ' . $totalPages;
        }
        echo '</p>';

        echo '<table class="inline">';
        echo '<thead><tr>';
        echo '<th>Usuario</th>';
        echo '<th>Nombre</th>';
        echo '<th>Grupos</th>';
        echo '<th>Inicia con "sie"</th>';
        echo '<th>Tiene "manager"</th>';
        echo '<th>Tiene "sie"</th>';
        echo '<th>Estado</th>';
        echo '</tr></thead>';
        echo '<tbody>';

        foreach ($usersSlice as $username => $userinfo) {
            $groups = isset($userinfo['grps']) ? $userinfo['grps'] : [];
            $groupsStr = implode(', ', $groups);
            $startsWithSie = (stripos($username, 'sie') === 0);
            $hasManager = in_array('manager', $groups);
            $hasSieGroup = in_array('sie', $groups);

            // Determinar estado
            // Regla: sie* DEBE tener manager+sie. No-sie NO puede tener manager (pero si puede tener sie)
            if ($startsWithSie && $hasManager && $hasSieGroup) {
                $status = '<span style="color: green;">&#10004; Correcto</span>';
                $rowClass = '';
            } elseif ($startsWithSie && (!$hasManager || !$hasSieGroup)) {
                $missing = [];
                if (!$hasManager) $missing[] = 'manager';
                if (!$hasSieGroup) $missing[] = 'sie';
                $status = '<span style="color: orange;">&#9888; Falta: ' . implode(', ', $missing) . '</span>';
                $rowClass = 'style="background-color: #fff3cd;"';
            } elseif (!$startsWithSie && $hasManager) {
                $status = '<span style="color: red;">&#10008; Manager no permitido</span>';
                $rowClass = 'style="background-color: #f8d7da;"';
            } else {
                $status = '<span style="color: green;">&#10004; Correcto</span>';
                $rowClass = '';
            }

            echo '<tr ' . $rowClass . '>';
            echo '<td><strong>' . hsc($username) . '</strong></td>';
            echo '<td>' . hsc($userinfo['name'] ?? '') . '</td>';
            echo '<td>' . hsc($groupsStr) . '</td>';
            echo '<td>' . ($startsWithSie ? 'Si' : 'No') . '</td>';
            echo '<td>' . ($hasManager ? 'Si' : 'No') . '</td>';
            echo '<td>' . ($hasSieGroup ? 'Si' : 'No') . '</td>';
            echo '<td>' . $status . '</td>';
            echo '</tr>';
        }

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

        // Controles de paginacion
        if ($totalPages > 1) {
            $baseUrl = wl($ID, ['do' => 'admin', 'page' => 'managerprotect']);
            echo '<div class="pagenav" style="margin: 10px 0;">';
            if ($currentPage > 1) {
                echo '<a href="' . $baseUrl . '&pg=' . ($currentPage - 1) . '" class="button">&laquo; Anterior</a> ';
            }
            for ($p = 1; $p <= $totalPages; $p++) {
                if ($p == $currentPage) {
                    echo '<strong style="margin: 0 4px;">[' . $p . ']</strong>';
                } else {
                    echo '<a href="' . $baseUrl . '&pg=' . $p . '" style="margin: 0 4px;">' . $p . '</a>';
                }
            }
            if ($currentPage < $totalPages) {
                echo ' <a href="' . $baseUrl . '&pg=' . ($currentPage + 1) . '" class="button">Siguiente &raquo;</a>';
            }
            echo '</div>';
        }

        // Boton de sincronizacion
        echo '<h2>Sincronizar Usuarios</h2>';
        echo '<p>Haga clic en el boton para aplicar las reglas a todos los usuarios existentes:</p>';

        echo '<form method="post" action="' . wl($ID, ['do' => 'admin', 'page' => 'managerprotect']) . '">';
        echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
        echo '<button type="submit" name="sync_users" class="button">Sincronizar Todos los Usuarios</button>';
        echo '</form>';

        // Estilos
        echo '<style>
            .plugin_managerprotect table { margin: 20px 0; }
            .plugin_managerprotect .button {
                background: #3498db;
                color: white;
                border: none;
                padding: 10px 20px;
                border-radius: 5px;
                cursor: pointer;
                font-size: 1em;
            }
            .plugin_managerprotect .button:hover {
                background: #2980b9;
            }
        </style>';
    }
}
