<?php
if (!defined('DOKU_INC')) die();

class action_plugin_fuzzysearch extends DokuWiki_Action_Plugin {
    private function getCacheFile() {
        $user = $this->getCurrentUser();
        if (!$user) return null;
        $userHash = md5($user);
        return DOKU_INC . 'data/cache/fuzzysearch_pages_' . $userHash . '.json';
    }

    private function getCacheMetaFile() {
        $user = $this->getCurrentUser();
        if (!$user) return null;
        $userHash = md5($user);
        return DOKU_INC . 'data/cache/fuzzysearch_pages_' . $userHash . '.meta.json';
    }

    public function register(Doku_Event_Handler $controller) {
        $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'handle_ajax_call');
        $controller->register_hook('INDEXER_VERSION_GET', 'BEFORE', $this, 'update_cache_on_change');
        $controller->register_hook('TPL_METAHEADER_OUTPUT', 'BEFORE', $this, 'load_scripts');
    }

    public function handle_ajax_call(Doku_Event &$event, $param) {
        if ($event->data === 'fuzzysearch_pages') {
            $event->preventDefault();
            $event->stopPropagation();

            if (!$this->isLoggedIn()) {
                $this->redirectToLogin();
                exit;
            }

            $cacheFile = $this->getCacheFile();
            $this->ensureCacheExists();

            if (file_exists($cacheFile)) {
                header('Content-Type: application/json');
                readfile($cacheFile);
            } else {
                header('HTTP/1.1 500 Internal Server Error');
                echo json_encode(['error' => 'Cache generation failed']);
            }
            exit;
        }
    }

    public function update_cache_on_change(Doku_Event &$event, $param) {
        if ($this->isLoggedIn()) {
            $this->ensureCacheExists(true);
        }
    }

    public function load_scripts(Doku_Event &$event, $param) {
        error_log('FuzzySearch: Loading scripts');
        // Load Fuse.js
        $fuseSrc = DOKU_BASE . 'lib/plugins/fuzzysearch/fuse.min.js';
        if (!in_array($fuseSrc, array_column($event->data['script'], 'src'))) {
            $event->data['script'][] = [
                'type' => 'text/javascript',
                'src' => $fuseSrc,
                '_data' => '',
                'defer' => 'defer'
            ];
            error_log('FuzzySearch: Fuse.js added');
        }

        // Load search bar script
        $scriptSrc = DOKU_BASE . 'lib/plugins/fuzzysearch/script.js';
        if (!in_array($scriptSrc, array_column($event->data['script'], 'src'))) {
            $event->data['script'][] = [
                'type' => 'text/javascript',
                'src' => $scriptSrc,
                '_data' => '',
                'defer' => 'defer'
            ];
            error_log('FuzzySearch: script.js added');
        }

        // Load editor enhancement script
        $editorSrc = DOKU_BASE . 'lib/plugins/fuzzysearch/editor.js';
        if (!in_array($editorSrc, array_column($event->data['script'], 'src'))) {
            $event->data['script'][] = [
                'type' => 'text/javascript',
                'src' => $editorSrc,
                '_data' => '',
                'defer' => 'defer'
            ];
            error_log('FuzzySearch: editor.js added');
        }
    }

    private function ensureCacheExists($forceUpdate = false) {
        $cacheFile = $this->getCacheFile();
        $cacheMetaFile = $this->getCacheMetaFile();
        if (!$cacheFile || !$cacheMetaFile) return;

        $meta = $this->loadCacheMeta($cacheMetaFile);
        $lastModified = $this->getLastPageModificationTime();

        if (!$forceUpdate && file_exists($cacheFile) && isset($meta['last_updated']) && $meta['last_updated'] >= $lastModified) {
            return;
        }

        $pages = $this->generatePageList();
        $jsonData = json_encode($pages);
        $metaData = ['last_updated' => time()];

        if (!is_dir(dirname($cacheFile))) {
            mkdir(dirname($cacheFile), 0755, true);
        }

        file_put_contents($cacheFile, $jsonData);
        file_put_contents($cacheMetaFile, json_encode($metaData));
    }

    private function generatePageList() {
        if (!$this->isLoggedIn()) {
            $this->redirectToLogin();
            exit;
        }

        $dir = DOKU_INC . 'data/pages/';
        $page_list = $this->getPageList($dir);
        $pages = [];
        foreach ($page_list as $file) {
            $id = pathID($file);
            if (auth_quickaclcheck($id) >= AUTH_READ) {
                $title = p_get_first_heading($id) ?: noNS($id);
                $pages[] = ['id' => $id, 'title' => $title];
            }
        }
        return $pages;
    }

    private function getPageList($dir, $base = '') {
        $files = [];
        $items = dir($dir);
        while (false !== ($entry = $items->read())) {
            if ($entry === '.' || $entry === '..') continue;
            $path = $dir . $entry;
            if (is_dir($path) && auth_quickaclcheck($base . $entry . ':') >= AUTH_READ) {
                $files = array_merge($files, $this->getPageList($path . '/', $base . $entry . ':'));
            } elseif (preg_match('/\.txt$/', $entry)) {
                $files[] = $base . substr($entry, 0, -4);
            }
        }
        $items->close();
        return $files;
    }

    private function getLastPageModificationTime() {
        $dir = DOKU_INC . 'data/pages/';
        $latest = 0;
        $this->scanDirForLatest($dir, $latest);
        return $latest;
    }

    private function scanDirForLatest($dir, &$latest) {
        $items = dir($dir);
        while (false !== ($entry = $items->read())) {
            if ($entry === '.' || $entry === '..') continue;
            $path = $dir . $entry;
            if (is_dir($path)) {
                $this->scanDirForLatest($path . '/', $latest);
            } elseif (preg_match('/\.txt$/', $entry)) {
                $mtime = filemtime($path);
                if ($mtime > $latest) $latest = $mtime;
            }
        }
        $items->close();
    }

    private function loadCacheMeta($file) {
        if (file_exists($file)) {
            return json_decode(file_get_contents($file), true) ?: [];
        }
        return [];
    }

    private function isLoggedIn() {
        return !empty($_SERVER['REMOTE_USER']);
    }

    private function getCurrentUser() {
        return $_SERVER['REMOTE_USER'] ?? null;
    }

    private function redirectToLogin() {
        global $ID;
        $loginUrl = wl('', ['do' => 'login', 'id' => $ID], true, '&');
        header("Location: $loginUrl");
    }
}