/**
 * WorkflowGenerationPipeline — workflow-driven code generation
 *
 * Delegates to WorkflowExecutor (backed by VL-Workflow-Engine) for execution.
 * The workflow JSON (e.g., meta-direct-codegen.json) defines the DAG of LLM
 * calls, loops, branches, and file writes.
 *
 * Fallback: If workflow execution fails, transparently falls back
 * to the original JS GenerationPipeline.
 */
import path from 'path';
import fs from 'fs';
import { resolveSeedWorkflowDir } from '../utils/bundle-paths.js';

// Workflow file search order for generation (large/complex projects)
const GENERATION_WORKFLOW_NAMES = [
  'fullstack-codegen-v2.json',
  'fullstack-codegen.json',
  'fullstack-parallel.json',
  '9-file-codegen.json',
  '3-file-codegen.json',
];

// Simplified workflow for small projects (Meta-direct path)
const META_DIRECT_WORKFLOW = 'meta-direct-codegen.json';

// Complexity threshold: below this, use meta-direct path
const SMALL_PROJECT_THRESHOLD = {
  maxPages: 5,
  maxServices: 3,
  maxDescriptionLength: 2000,
};

export class WorkflowGenerationPipeline {
  /**
   * @param {Object} config - VL-Code configuration
   */
  constructor(config) {
    this.config = config;
    this._fallbackPipeline = null;
  }

  /**
   * Run the generation pipeline.
   * @param {Object} params - { userRequest, targetLang, mode }
   * @param {Object} callbacks - { onStepStart, onStepDone, onFileWritten, onToken, onDone, onError, onMetadataReady }
   */
  async run(params, callbacks = {}) {
    const useMetaDirect = params.mode === 'meta-direct' || (
      params.mode !== 'auxiliary' && this._isSmallProject(params)
    );

    const workflowFile = useMetaDirect
      ? this._findWorkflowByName(META_DIRECT_WORKFLOW)
      : this._findGenerationWorkflow();

    if (workflowFile) {
      callbacks.onStepStart?.({
        id: 'mode-select',
        title: useMetaDirect ? 'Using Meta-Direct path (small project)' : 'Using auxiliary-file path (complex project)',
      });
      try {
        return await this._runViaEngine(workflowFile, params, callbacks);
      } catch (err) {
        callbacks.onError?.(`Engine execution failed (${err.message}), falling back to JS pipeline`);
      }
    }

    // Fallback to original JS pipeline
    return this._runFallback(params, callbacks);
  }

  /**
   * Execute the generation workflow via WorkflowExecutor (VL-Workflow-Engine).
   */
  async _runViaEngine(workflowFile, params, callbacks) {
    const cb = callbacks;
    const wfJson = JSON.parse(fs.readFileSync(workflowFile, 'utf-8'));

    cb.onStepStart?.({ id: 'engine', title: 'Starting workflow engine execution...' });

    const { WorkflowExecutor } = await import('./workflow-executor.js');
    const executor = new WorkflowExecutor(this.config);

    await executor.execute(wfJson, {
      userRequest: params.userRequest || '',
      targetLang: params.targetLang || 'en',
    }, {
      onNodeStart: (info) => {
        cb.onStepStart?.({ id: info.nodeId, title: info.title || info.nodeId });
      },
      onNodeDone: (info) => {
        cb.onStepDone?.({ id: info.nodeId, title: info.title || info.nodeId });
      },
      onNodeError: (info) => {
        cb.onError?.(`Step ${info.nodeId} failed: ${info.error || 'unknown error'}`);
      },
      onToolStart: (info) => cb.onToolStart?.(info),
      onToolDone: (info) => cb.onToolDone?.(info),
      onToolError: (info) => cb.onToolError?.(info),
      onToolMessage: (info) => cb.onToolMessage?.(info),
      onToken: (token) => cb.onToken?.(token),
      onFileWritten: (fp) => cb.onFileWritten?.(fp),
      onDone: (summary) => {
        cb.onDone?.({
          filesWritten: summary.filesWritten,
          stageCount: summary.stageCount,
        });
      },
      onError: (msg) => cb.onError?.(msg),
      onText: (text) => {
        cb.onText?.(text);
        cb.onToken?.(text);
      },
    });
  }

  /**
   * Fallback to the original JS GenerationPipeline.
   */
  async _runFallback(params, callbacks) {
    if (!this._fallbackPipeline) {
      const { GenerationPipeline } = await import('./generation-pipeline.js');
      this._fallbackPipeline = new GenerationPipeline(this.config);
    }
    return this._fallbackPipeline.run(params, callbacks);
  }

  /**
   * Find a generation workflow JSON file.
   */
  _findGenerationWorkflow() {
    const searchDirs = [
      path.join(this.config.workDir, '.vl-code', 'workflows'),
      resolveSeedWorkflowDir(),
    ];

    for (const dir of searchDirs) {
      for (const name of GENERATION_WORKFLOW_NAMES) {
        const candidate = path.join(dir, name);
        if (fs.existsSync(candidate)) return candidate;
      }
    }

    return null;
  }

  /**
   * Find a specific workflow by name.
   */
  _findWorkflowByName(name) {
    const searchDirs = [
      path.join(this.config.workDir, '.vl-code', 'workflows'),
      resolveSeedWorkflowDir(),
    ];
    for (const dir of searchDirs) {
      const candidate = path.join(dir, name);
      if (fs.existsSync(candidate)) return candidate;
    }
    return null;
  }

  /**
   * Heuristic to determine if a project is "small" (use Meta-direct path).
   */
  _isSmallProject(params) {
    const req = params.userRequest || '';
    if (req.length > SMALL_PROJECT_THRESHOLD.maxDescriptionLength) return false;

    const pageKeywords = req.match(/page|screen|view|form|list|dashboard/gi) || [];
    const serviceKeywords = req.match(/service|api|backend|database/gi) || [];

    if (pageKeywords.length > SMALL_PROJECT_THRESHOLD.maxPages) return false;
    if (serviceKeywords.length > SMALL_PROJECT_THRESHOLD.maxServices) return false;

    return true;
  }
}
