import { watch, existsSync } from 'fs';
import { join, extname, basename } from 'path';

let watcher = null;
const DEBOUNCE_MS = 800;
const COOLDOWN_MS = 5000;

// Supported extensions for auto-ingest from watched folders
const WATCH_FOLDER_EXTS = new Set(['.txt', '.md', '.html', '.htm', '.pdf', '.json', '.csv']);

// Per-folder watchers for auto-ingest
const folderWatchers = new Map();
const folderFileTimers = new Map();
const folderFileCooldowns = new Map();

// Per-file debounce timers and cooldown tracking
const fileTimers = new Map();
const fileCooldowns = new Map();

export function startWatching(dirPath, onChange) {
  if (watcher) stopWatching();
  if (typeof onChange !== 'function') throw new Error('onChange callback required');

  watcher = watch(dirPath, { recursive: true }, (eventType, filename) => {
    if (!filename || filename.startsWith('.') || filename.endsWith('~') || filename.endsWith('.tmp')) return;

    // Skip if this file is in cooldown (already fired recently)
    const now = Date.now();
    const lastFired = fileCooldowns.get(filename);
    if (lastFired && (now - lastFired) < COOLDOWN_MS) return;

    // Per-file debounce: reset timer for this specific file
    if (fileTimers.has(filename)) clearTimeout(fileTimers.get(filename));

    fileTimers.set(filename, setTimeout(() => {
      fileTimers.delete(filename);
      fileCooldowns.set(filename, Date.now());
      try {
        onChange(eventType, filename, join(dirPath, filename));
      } catch (err) {
        console.error(`[file-watcher] Callback error: ${err.message}`);
      }
    }, DEBOUNCE_MS));
  });

  watcher.on('error', (err) => {
    console.error(`[file-watcher] Error: ${err.message}`);
  });

  console.log(`[file-watcher] Watching: ${dirPath}`);
  return true;
}

export function stopWatching() {
  // Clear all per-file timers
  for (const timer of fileTimers.values()) clearTimeout(timer);
  fileTimers.clear();
  fileCooldowns.clear();

  if (watcher) {
    watcher.close();
    watcher = null;
    console.log('[file-watcher] Stopped watching');
  }
}

export function isWatching() {
  return watcher !== null;
}

/**
 * Start watching a list of folders for new ingestible files.
 * Only fires for new files (rename events where the file now exists).
 * Supported extensions: .txt, .md, .html, .htm, .pdf, .json, .csv
 *
 * @param {string[]} folders - Array of absolute folder paths to watch
 * @param {function} onNewFile - Called with (filePath, filename) for each new ingestible file
 */
export function startWatchFolders(folders, onNewFile) {
  stopWatchFolders();
  if (!Array.isArray(folders) || folders.length === 0) return;
  if (typeof onNewFile !== 'function') throw new Error('onNewFile callback required');

  for (const folder of folders) {
    if (!folder || typeof folder !== 'string') continue;
    if (!existsSync(folder)) {
      console.warn(`[file-watcher] Watch folder not found, skipping: ${folder}`);
      continue;
    }

    try {
      const fw = watch(folder, { recursive: false }, (eventType, filename) => {
        if (!filename) return;
        if (filename.startsWith('.') || filename.endsWith('~') || filename.endsWith('.tmp')) return;

        const ext = extname(filename).toLowerCase();
        if (!WATCH_FOLDER_EXTS.has(ext)) return;

        // Only trigger on 'rename' (create/delete), not 'change' (modify)
        if (eventType !== 'rename') return;

        const filePath = join(folder, filename);

        // Verify the file exists (rename fires on delete too)
        if (!existsSync(filePath)) return;

        const key = filePath;
        const now = Date.now();
        const lastFired = folderFileCooldowns.get(key);
        if (lastFired && (now - lastFired) < COOLDOWN_MS) return;

        if (folderFileTimers.has(key)) clearTimeout(folderFileTimers.get(key));

        folderFileTimers.set(key, setTimeout(() => {
          folderFileTimers.delete(key);
          folderFileCooldowns.set(key, Date.now());
          // Verify again after debounce
          if (!existsSync(filePath)) return;
          try {
            onNewFile(filePath, basename(filename));
          } catch (err) {
            console.error(`[file-watcher] onNewFile callback error: ${err.message}`);
          }
        }, DEBOUNCE_MS));
      });

      fw.on('error', (err) => {
        console.error(`[file-watcher] Folder watcher error for ${folder}: ${err.message}`);
      });

      folderWatchers.set(folder, fw);
      console.log(`[file-watcher] Watching folder for auto-ingest: ${folder}`);
    } catch (err) {
      console.warn(`[file-watcher] Could not watch folder ${folder}: ${err.message}`);
    }
  }
}

export function stopWatchFolders() {
  for (const timer of folderFileTimers.values()) clearTimeout(timer);
  folderFileTimers.clear();
  folderFileCooldowns.clear();

  for (const [folder, fw] of folderWatchers.entries()) {
    try { fw.close(); } catch {}
    console.log(`[file-watcher] Stopped watching folder: ${folder}`);
  }
  folderWatchers.clear();
}

export function getWatchedFolders() {
  return [...folderWatchers.keys()];
}
