// v2.8.0: Workflow file discovery — walks project-local and global workflow
// directories to find runnable scripts. Built-in workflows from the catalog
// are merged into the results (they take precedence over user-defined files).
// All functions exported for testing.

import { readdirSync, existsSync } from 'node:fs';
import { join, basename, extname } from 'node:path';
import { homedir } from 'node:os';
import { getBuiltinWorkflows, getBuiltinWorkflow } from './catalog.js';

/**
 * Sentinel prefix used in `sourceFile` for built-in workflows from the
 * catalog so callers (e.g. WorkflowManager) can detect and handle them
 * by calling `generateScript()` instead of reading a file from disk.
 */
const BUILTIN_PREFIX = 'builtin:';

/**
 * Metadata about a discovered workflow file (or built-in workflow).
 */
export interface WorkflowMeta {
  /** Workflow name (file stem without .js extension). */
  name: string;
  /** Description loaded from the workflow module's `meta.description`.
   *  Empty string until loadWorkflowMeta() resolves it. */
  description: string;
  /** Absolute path to the .js file.
   *  For built-in workflows this is `'builtin:<name>'` — the caller
   *  should use `getBuiltinWorkflow(name)` and `generateScript()`
   *  instead of reading this path from disk. */
  sourceFile: string;
}

/**
 * Test whether a `WorkflowMeta.sourceFile` points to a built-in workflow
 * (rather than a file on disk).
 *
 * @param meta - The workflow metadata to check.
 */
export function isBuiltinWorkflow(meta: WorkflowMeta): boolean {
  return meta.sourceFile.startsWith(BUILTIN_PREFIX);
}

/**
 * Find all workflow .js files in the standard search paths, merged with
 * built-in workflows from the catalog.
 *
 * Priority order (first match wins for same-named workflows):
 *  1. Built-in catalog (always takes precedence)
 *  2. <projectRoot>/.boocode/workflows/   (project-local)
 *  3. ~/.boocode/workflows/               (global, per-user)
 *
 * @param projectRoot - Absolute path to the current project root.
 */
export function discoverWorkflows(projectRoot: string): WorkflowMeta[] {
  const seen = new Set<string>();
  const results: WorkflowMeta[] = [];

  // 1. Built-in workflows (highest priority)
  for (const builtin of getBuiltinWorkflows()) {
    seen.add(builtin.name);
    results.push({
      name: builtin.name,
      description: builtin.description,
      sourceFile: `${BUILTIN_PREFIX}${builtin.name}`,
    });
  }

  // 2. Project-local + global file-based workflows
  const dirs = [
    join(projectRoot, '.boocode', 'workflows'),
    join(homedir(), '.boocode', 'workflows'),
  ];

  for (const dir of dirs) {
    if (!existsSync(dir)) continue;
    try {
      const entries = readdirSync(dir);
      for (const f of entries) {
        if (!f.endsWith('.js')) continue;
        const name = basename(f, '.js');
        if (seen.has(name)) continue; // built-in shadows project-local,
        // project-local shadows global
        seen.add(name);
        results.push({
          name,
          description: '',
          sourceFile: join(dir, f),
        });
      }
    } catch {
      // Permission error on directory — skip silently
      continue;
    }
  }

  return results;
}

/**
 * Find a single workflow by name across built-in catalog and search paths.
 *
 * Priority: built-in > project-local > global.
 *
 * @param name - Workflow name (without .js extension).
 * @param projectRoot - Absolute path to the current project root.
 */
export function findWorkflow(
  name: string,
  projectRoot: string,
): WorkflowMeta | undefined {
  const builtin = getBuiltinWorkflow(name);
  if (builtin) {
    return {
      name: builtin.name,
      description: builtin.description,
      sourceFile: `${BUILTIN_PREFIX}${builtin.name}`,
    };
  }

  // Fall back to file-based discovery
  return discoverWorkflows(projectRoot).find((w) => w.name === name);
}

/**
 * Validate a candidate workflow file path.
 * Checks that the file exists and has a .js extension.
 *
 * @param filePath - Absolute path to check.
 */
export function isValidWorkflowPath(filePath: string): boolean {
  return extname(filePath) === '.js' && existsSync(filePath);
}
