"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.ALWAYS_IGNORED = void 0;
exports.startWatcher = startWatcher;
const chokidar_1 = __importDefault(require("chokidar"));
const micromatch_1 = __importDefault(require("micromatch"));
const path_1 = __importDefault(require("path"));
const logger_1 = require("../lib/logger");
const log = (0, logger_1.createLogger)('watcher');
/** Directory basenames that are always excluded from watching and scanning at any nesting level. */
exports.ALWAYS_IGNORED = new Set([
    'node_modules', '.git', '.hg', '.svn',
    '.next', '.nuxt', '.turbo',
    'dist', 'build',
    '.graph-memory', '.notes', '.tasks', '.skills',
]);
// chokidar 5: watch the directory directly — glob patterns don't fire 'add' for existing files
function startWatcher(dir, handlers, pattern = '**/*.md', excludePatterns) {
    const matches = (filePath) => {
        const rel = path_1.default.relative(dir, filePath);
        if (excludePatterns && excludePatterns.length > 0 && micromatch_1.default.isMatch(rel, excludePatterns))
            return false;
        return micromatch_1.default.isMatch(rel, pattern);
    };
    const ignored = (filePath) => {
        const basename = path_1.default.basename(filePath);
        // Skip dotfiles and dotdirs (hidden) at any level — except the watched root itself
        if (basename.startsWith('.') && filePath !== dir)
            return true;
        // Always-ignored directories by basename at any nesting level
        if (exports.ALWAYS_IGNORED.has(basename))
            return true;
        // User-defined exclude patterns (glob-based) — only prune directories, not individual files.
        // File-level filtering is handled by matches() + dispatchAdd per-graph excludes.
        if (excludePatterns && excludePatterns.length > 0) {
            const rel = path_1.default.relative(dir, filePath);
            if (micromatch_1.default.isMatch(rel + '/x', excludePatterns))
                return true;
        }
        return false;
    };
    let resolveReady;
    const whenReady = new Promise(resolve => {
        resolveReady = resolve;
    });
    const watcher = chokidar_1.default.watch(dir, {
        ignoreInitial: false,
        persistent: true,
        ignored,
    });
    watcher.on('add', (filePath) => {
        if (matches(filePath))
            handlers.onAdd(filePath);
    });
    watcher.on('change', (filePath) => {
        if (matches(filePath))
            handlers.onChange(filePath);
    });
    watcher.on('unlink', (filePath) => {
        if (matches(filePath))
            handlers.onUnlink(filePath);
    });
    watcher.on('ready', () => {
        log.info({ dir }, 'Ready. Watching');
        resolveReady();
    });
    watcher.on('error', (err) => {
        // Silently ignore symlink loops — nothing useful to watch there
        if (err instanceof Error && err.code === 'ELOOP')
            return;
        log.error({ err }, 'Watch error');
    });
    return {
        whenReady,
        close: () => watcher.close(),
    };
}
