/**
 * Help Routes Module
 * Handles all Help-related API endpoints for command guide and CodexLens docs
 */
import { readFileSync, existsSync, watch } from 'fs';
import { join, normalize, relative, resolve, sep } from 'path';
import { homedir } from 'os';
import type { RouteContext } from './types.js';

/**
 * Get the ccw-help command.json file path (pure function)
 * Priority: project path (.claude/skills/ccw-help/command.json) > user path (~/.claude/skills/ccw-help/command.json)
 * @param projectPath - The project path to check first
 */
function getCommandFilePath(projectPath: string | null): string | null {
  // Try project path first
  if (projectPath) {
    const projectFilePath = join(projectPath, '.claude', 'skills', 'ccw-help', 'command.json');
    if (existsSync(projectFilePath)) {
      return projectFilePath;
    }
  }

  // Fall back to user path
  const userFilePath = join(homedir(), '.claude', 'skills', 'ccw-help', 'command.json');
  if (existsSync(userFilePath)) {
    return userFilePath;
  }

  return null;
}

// ========== In-Memory Cache ==========
interface CacheEntry {
  data: any;
  timestamp: number;
}

const cache = new Map<string, CacheEntry>();
const CACHE_TTL = 300000; // 5 minutes

/**
 * Get cached data or load from file
 */
function getCachedData(key: string, filePath: string): any {
  const now = Date.now();
  const cached = cache.get(key);

  // Return cached data if valid
  if (cached && (now - cached.timestamp) < CACHE_TTL) {
    return cached.data;
  }

  // Load fresh data
  try {
    if (!existsSync(filePath)) {
      console.error(`Help data file not found: ${filePath}`);
      return null;
    }

    const content = readFileSync(filePath, 'utf8');
    const data = JSON.parse(content);

    // Update cache
    cache.set(key, { data, timestamp: now });

    return data;
  } catch (error) {
    console.error(`Failed to load help data from ${filePath}:`, error);
    return null;
  }
}

/**
 * Invalidate cache for a specific key
 */
function invalidateCache(key: string): void {
  cache.delete(key);
  console.log(`Cache invalidated: ${key}`);
}

// ========== File Watchers ==========
let watchersInitialized = false;

/**
 * Initialize file watcher for command.json
 * @param projectPath - The project path to resolve command file
 */
function initializeFileWatchers(projectPath: string | null): void {
  if (watchersInitialized) return;

  const commandFilePath = getCommandFilePath(projectPath);

  if (!commandFilePath) {
    console.warn(`ccw-help command.json not found in project or user paths`);
    return;
  }

  try {
    // Watch the command.json file
    const watcher = watch(commandFilePath, (eventType) => {
      console.log(`File change detected: command.json (${eventType})`);

      // Invalidate all cache entries when command.json changes
      invalidateCache('command-data');
    });

    watchersInitialized = true;
    (watcher as any).unref?.();
    console.log(`File watcher initialized for: ${commandFilePath}`);
  } catch (error) {
    console.error('Failed to initialize file watcher:', error);
  }
}

// ========== Helper Functions ==========

/**
 * Get command data from command.json (with caching)
 */
function getCommandData(projectPath: string | null): any {
  const filePath = getCommandFilePath(projectPath);
  if (!filePath) return null;

  return getCachedData('command-data', filePath);
}

/**
 * Filter commands by search query
 */
function filterCommands(commands: any[], query: string): any[] {
  if (!query) return commands;

  const lowerQuery = query.toLowerCase();
  return commands.filter(cmd =>
    cmd.name?.toLowerCase().includes(lowerQuery) ||
    cmd.command?.toLowerCase().includes(lowerQuery) ||
    cmd.description?.toLowerCase().includes(lowerQuery) ||
    cmd.category?.toLowerCase().includes(lowerQuery)
  );
}

/**
 * Category merge mapping for frontend compatibility
 * Merges additional categories into target category for display
 * Format: { targetCategory: [additionalCategoriesToMerge] }
 */
const CATEGORY_MERGES: Record<string, string[]> = {
  'cli': ['general'],  // CLI tab shows both 'cli' and 'general' commands
};

/**
 * Group commands by category with subcategories
 */
function groupCommandsByCategory(commands: any[]): any {
  const grouped: any = {};

  for (const cmd of commands) {
    const category = cmd.category || 'general';
    const subcategory = cmd.subcategory || null;

    if (!grouped[category]) {
      grouped[category] = {
        name: category,
        commands: [],
        subcategories: {}
      };
    }

    if (subcategory) {
      if (!grouped[category].subcategories[subcategory]) {
        grouped[category].subcategories[subcategory] = [];
      }
      grouped[category].subcategories[subcategory].push(cmd);
    } else {
      grouped[category].commands.push(cmd);
    }
  }

  // Apply category merges for frontend compatibility
  for (const [target, sources] of Object.entries(CATEGORY_MERGES)) {
    // Initialize target category if not exists
    if (!grouped[target]) {
      grouped[target] = {
        name: target,
        commands: [],
        subcategories: {}
      };
    }

    // Merge commands from source categories into target
    for (const source of sources) {
      if (grouped[source]) {
        // Merge direct commands
        grouped[target].commands = [
          ...grouped[target].commands,
          ...grouped[source].commands
        ];
        // Merge subcategories
        for (const [subcat, cmds] of Object.entries(grouped[source].subcategories)) {
          if (!grouped[target].subcategories[subcat]) {
            grouped[target].subcategories[subcat] = [];
          }
          grouped[target].subcategories[subcat] = [
            ...grouped[target].subcategories[subcat],
            ...(cmds as any[])
          ];
        }
      }
    }
  }

  return grouped;
}

/**
 * Build workflow relationships from command flow data
 */
function buildWorkflowRelationships(commands: any[]): any {
  const relationships: any = {
    workflows: [],
    dependencies: {},
    alternatives: {}
  };

  for (const cmd of commands) {
    if (!cmd.flow) continue;

    const cmdName = cmd.command;

    // Build next_steps relationships
    if (cmd.flow.next_steps) {
      if (!relationships.dependencies[cmdName]) {
        relationships.dependencies[cmdName] = { next: [], prev: [] };
      }
      relationships.dependencies[cmdName].next = cmd.flow.next_steps;

      // Add reverse relationship
      for (const nextCmd of cmd.flow.next_steps) {
        if (!relationships.dependencies[nextCmd]) {
          relationships.dependencies[nextCmd] = { next: [], prev: [] };
        }
        if (!relationships.dependencies[nextCmd].prev.includes(cmdName)) {
          relationships.dependencies[nextCmd].prev.push(cmdName);
        }
      }
    }

    // Build prerequisites relationships
    if (cmd.flow.prerequisites) {
      if (!relationships.dependencies[cmdName]) {
        relationships.dependencies[cmdName] = { next: [], prev: [] };
      }
      relationships.dependencies[cmdName].prev = [
        ...new Set([...relationships.dependencies[cmdName].prev, ...cmd.flow.prerequisites])
      ];
    }

    // Build alternatives
    if (cmd.flow.alternatives) {
      relationships.alternatives[cmdName] = cmd.flow.alternatives;
    }

    // Add to workflows list
    if (cmd.category === 'workflow') {
      relationships.workflows.push({
        name: cmd.name,
        command: cmd.command,
        description: cmd.description,
        flow: cmd.flow
      });
    }
  }

  return relationships;
}

// ========== API Routes ==========

/**
 * Handle Help routes
 * @returns true if route was handled, false otherwise
 */
export async function handleHelpRoutes(ctx: RouteContext): Promise<boolean> {
  const { pathname, url, req, res, initialPath } = ctx;

  // Initialize file watchers on first request
  initializeFileWatchers(initialPath);

  // API: Get all commands with optional search
  if (pathname === '/api/help/commands') {
    const commandData = getCommandData(initialPath);
    if (!commandData) {
      res.writeHead(404, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'ccw-help command.json not found' }));
      return true;
    }

    const searchQuery = url.searchParams.get('q') || '';
    let commands = commandData.commands || [];

    // Filter by search query if provided
    if (searchQuery) {
      commands = filterCommands(commands, searchQuery);
    }

    // Group by category
    const grouped = groupCommandsByCategory(commands);

    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
      commands: commands,
      grouped: grouped,
      total: commands.length,
      essential: commandData.essential_commands || [],
      metadata: commandData._metadata
    }));
    return true;
  }

  // API: Get workflow command relationships
  if (pathname === '/api/help/workflows') {
    const commandData = getCommandData(initialPath);
    if (!commandData) {
      res.writeHead(404, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'ccw-help command.json not found' }));
      return true;
    }

    const commands = commandData.commands || [];
    const relationships = buildWorkflowRelationships(commands);

    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify(relationships));
    return true;
  }

  // API: Get commands by category
  if (pathname === '/api/help/commands/by-category') {
    const commandData = getCommandData(initialPath);
    if (!commandData) {
      res.writeHead(404, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'ccw-help command.json not found' }));
      return true;
    }

    const commands = commandData.commands || [];
    const byCategory = groupCommandsByCategory(commands);

    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
      categories: commandData.categories || [],
      grouped: byCategory
    }));
    return true;
  }

  // API: Get agents list
  if (pathname === '/api/help/agents') {
    const commandData = getCommandData(initialPath);
    if (!commandData) {
      res.writeHead(404, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'ccw-help command.json not found' }));
      return true;
    }

    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
      agents: commandData.agents || [],
      total: (commandData.agents || []).length
    }));
    return true;
  }

  // API: Get command document content by source path
  if (pathname === '/api/help/command-content') {
    const sourceParam = url.searchParams.get('source');
    if (!sourceParam) {
      res.writeHead(400, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'Missing source parameter' }));
      return true;
    }

    try {
      // Determine the source path's actual location:
      // The source in command.json is relative to .claude/skills/ccw-help/
      // E.g., "../../commands/cli/cli-init.md"
      // We need to resolve this against that actual location, not the project root

      const baseDir = initialPath || join(homedir(), '.claude');
      const commandJsonDir = join(baseDir, 'skills', 'ccw-help');

      // Resolve the source path against where command.json actually is
      const resolvedPath = resolve(commandJsonDir, sourceParam);

      // Normalize the path for the OS
      const normalizedPath = normalize(resolvedPath);

      // Security: Verify path is within base directory (prevent path traversal)
      const relPath = relative(baseDir, normalizedPath);
      if (relPath.startsWith('..') || relPath.startsWith('~')) {
        console.warn(`[help-content] Access denied: Path traversal attempt - ${relPath}`);
        res.writeHead(403, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: 'Access denied' }));
        return true;
      }

      console.log(`[help-content] Base directory: ${baseDir}`);
      console.log(`[help-content] Command.json dir: ${commandJsonDir}`);
      console.log(`[help-content] Source parameter: ${sourceParam}`);
      console.log(`[help-content] Attempting to load: ${normalizedPath}`);
      console.log(`[help-content] Relative path check: ${relPath}`);

      if (!existsSync(normalizedPath)) {
        console.warn(`[help-content] File not found: ${normalizedPath}`);
        res.writeHead(404, { 'Content-Type': 'application/json' });
        res.end(JSON.stringify({ error: 'Document not found' }));
        return true;
      }

      const content = readFileSync(normalizedPath, 'utf8');
      console.log(`[help-content] Successfully served: ${normalizedPath}`);

      res.writeHead(200, { 'Content-Type': 'text/markdown; charset=utf-8' });
      res.end(content);
    } catch (error) {
      console.error('[help-content] Error:', error);
      res.writeHead(500, { 'Content-Type': 'application/json' });
      res.end(JSON.stringify({ error: 'Failed to read document', message: (error as any).message }));
    }
    return true;
  }

  // API: Get CodexLens documentation metadata
  if (pathname === '/api/help/codexlens') {
    // Return CodexLens quick-start guide data
    const codexLensData = {
      title: 'CodexLens Quick Start',
      description: 'Fast code indexing and semantic search for large codebases',
      sections: [
        {
          title: 'Key Concepts',
          items: [
            {
              name: 'Indexing',
              description: 'CodexLens builds a semantic index of your codebase for fast retrieval',
              command: 'codex_lens(action="init", path=".")'
            },
            {
              name: 'Search Modes',
              description: 'Text search for exact matches, semantic search for concept-based queries',
              command: 'codex_lens(action="search", query="authentication logic", mode="semantic")'
            },
            {
              name: 'Symbol Navigation',
              description: 'Extract and navigate code symbols (functions, classes, interfaces)',
              command: 'codex_lens(action="symbol", file="path/to/file.py")'
            }
          ]
        },
        {
          title: 'Common Commands',
          items: [
            {
              name: 'Initialize Index',
              command: 'codex_lens(action="init", path=".")',
              description: 'Index the current directory'
            },
            {
              name: 'Text Search',
              command: 'codex_lens(action="search", query="function name", path=".")',
              description: 'Search for exact text matches'
            },
            {
              name: 'Semantic Search',
              command: 'codex_lens(action="search", query="user authentication", mode="semantic")',
              description: 'Search by concept or meaning'
            },
            {
              name: 'Check Status',
              command: 'codex_lens(action="status")',
              description: 'View indexing status for all projects'
            }
          ]
        },
        {
          title: 'Best Practices',
          items: [
            { description: 'Index large codebases (>500 files) for optimal performance' },
            { description: 'Use semantic search for exploratory tasks' },
            { description: 'Combine with smart_search for medium-sized projects' },
            { description: 'Re-index after major code changes'  }
          ]
        }
      ],
      links: [
        { text: 'Full Documentation', url: 'https://github.com/yourusername/codex-lens' },
        { text: 'Tool Selection Guide', url: '/.claude/rules/tool-selection.md' }
      ]
    };

    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify(codexLensData));
    return true;
  }

  return false;
}
