/**
 * VL Workflow Engine — Types & Constants
 * Ported from Go: workflow/types.go
 * Spec: v3.16
 */

// ─── Workflow Type ───────────────────────────────────────────
const WorkflowType = { IDE: 'IDE', Business: 'Business', Approval: 'Approval', Local: 'Local' };

// ─── Step Types (derived from step ID prefix) ────────────────
const StepType = {
  Service: 'Service', API: 'API', Component: 'Component', LLM: 'LLM',
  Set: 'Set', Write: 'Write', Download: 'Download', Unzip: 'Unzip',
  Pause: 'Pause', Branch: 'Branch', Loop: 'Loop', Stop: 'Stop', Noop: 'Noop',
  // Aliases — common in workflow JSONs
  Fork: 'Fork', Check: 'Check', Done: 'Done'
};

// Map aliases to their execution behavior
const STEP_ALIAS = { Fork: 'Noop', Check: 'Branch', Done: 'Stop' };

function getStepType(stepID) {
  const prefix = stepID.split('_')[0];
  if (!prefix) return null;
  // Direct match
  if (StepType[prefix]) return StepType[prefix];
  // No underscore — treat entire ID as type (e.g., "Done" with no suffix)
  if (!stepID.includes('_') && StepType[stepID]) return StepType[stepID];
  return null;
}

/** Get the execution behavior for a step type (resolves aliases). */
function resolveStepBehavior(stepType) {
  return STEP_ALIAS[stepType] || stepType;
}

// ─── Execution Status ────────────────────────────────────────
const ExecutionStatus = {
  Running: 'running', Completed: 'completed', Failed: 'failed',
  Stopped: 'stopped', Paused: 'paused'
};

// ─── Write Mode ──────────────────────────────────────────────
const WriteMode = {
  Overwrite: 'overwrite', FailIfExists: 'failIfExists',
  Append: 'append', Prepend: 'prepend'
};

// ─── Loop Mode ───────────────────────────────────────────────
const LoopMode = { Parallel: 'parallel', Serial: 'serial' };

// ─── Parallel Error Strategy ─────────────────────────────────
const ParallelErrorStrategy = {
  FailFast: 'failFast', CollectAll: 'collectAll', PartialSuccess: 'partialSuccess'
};

// ─── Run Event Types (v3.12+) ────────────────────────────────
const RunEventType = {
  WorkflowStart: 'workflow_start', WorkflowDone: 'workflow_done',
  WorkflowFailed: 'workflow_failed', WorkflowCancelled: 'workflow_cancelled',
  StepStart: 'step_start', StepDone: 'step_done',
  StepError: 'step_error', StepSkipped: 'step_skipped',
  StepPrint: 'step_print',
  LLMToken: 'llm_token', LLMDone: 'llm_done',
  // Extended LLM events (v3.15.1+)
  LLMThinking: 'llm_thinking',       // thinking/reasoning token stream
  LLMToolUse: 'llm_tool_use',        // tool_use block from LLM
  LLMToolResult: 'llm_tool_result',  // tool execution result fed back
  LLMError: 'llm_error',             // LLM call error with details
  FileStart: 'file_start', FileDone: 'file_done',
  PauseStart: 'pause_start', PauseResumed: 'pause_resumed',
  PauseTimeout: 'pause_timeout', PauseRejected: 'pause_rejected',
  // Variable change tracking
  VarChanged: 'var_changed',
  // User-initiated pause (graceful stop after current step)
  WorkflowPaused: 'workflow_paused'
};

// ─── Supported Versions ──────────────────────────────────────
const SUPPORTED_VERSIONS = new Set(['3.6','3.7','3.8','3.9','3.10','3.12','3.13','3.14','3.15','3.16']);

function isV310OrLater(version) {
  return ['3.10','3.12','3.13','3.14','3.15','3.16'].includes(version);
}

/** Reserved keywords that cannot be used as step IDs. */
const RESERVED_NEXT_KEYWORDS = new Set(['RETURN', 'STOP', 'BREAK']);

// ─── LLM Error ───────────────────────────────────────────────
class LLMError extends Error {
  constructor({ type, code, message, retryable, statusCode, provider, model, requestId, providerError, details, raw }) {
    super(message);
    this.name = 'LLMError';
    this.type = type || 'unknown';
    this.code = code || '';
    this.retryable = retryable || false;
    this.statusCode = statusCode || 0;
    this.provider = provider || '';
    this.model = model || '';
    this.requestId = requestId || '';
    this.providerError = providerError || '';
    this.details = details || {};
    this.raw = raw || {};
  }
  toMap() {
    return {
      type: this.type, code: this.code, message: this.message,
      retryable: this.retryable, status_code: this.statusCode,
      provider: this.provider, model: this.model,
      request_id: this.requestId, provider_error: this.providerError,
      details: this.details, raw: this.raw
    };
  }
}

function buildErrorMap(err) {
  if (err instanceof LLMError) return err.toMap();
  return { type: 'internal_error', code: 'INTERNAL_ERROR', message: err.message || String(err) };
}

// ─── Execution Context ───────────────────────────────────────
class ExecutionContext {
  constructor({ workflowID, params = {}, paramTypes = {}, variables = {}, varTypes = {},
                systemVars = {}, localVars = {}, artifacts = {},
                adapters = {}, runParams = null, version = '3.15' }) {
    this.workflowID = workflowID;
    this.params = params;           // read-only
    this.paramTypes = paramTypes;
    this.variables = variables;     // $prefix
    this.varTypes = varTypes;
    this.systemVars = systemVars;   // SYSVAR.xxx
    this.localVars = localVars;     // _item, _index, _result, _meta, _error
    this.artifacts = artifacts;
    this.currentStepID = null;
    this.status = ExecutionStatus.Running;
    this.startTime = Date.now();
    this.version = version;
    // Adapters
    this.serviceAdapter = adapters.service || null;
    this.apiAdapter = adapters.api || null;
    this.componentAdapter = adapters.component || null;
    this.llmAdapter = adapters.llm || null;
    this.fileAdapter = adapters.file || null;
    this.docAdapter = adapters.doc || null;
    // Pause state (v3.15+)
    this.pauseState = null;
    this.runParams = runParams;
    // Event stream — consumers listen via onEvent callback
    this._eventSeq = 0;
    this._eventListeners = [];
    // Abort controller
    this._abortController = new AbortController();
    // Checkpoint: track completed step IDs
    this._completedSteps = [];
    // Phase 2: parallel branch completion tracking { parentStepID: Set<childID> }
    this._completedBranches = new Map();
    // Phase 2: loop iteration progress { loopStepID: completedCount }
    this._loopProgress = new Map();
  }

  get signal() { return this._abortController.signal; }
  abort() { this._abortController.abort(); }
  get aborted() { return this._abortController.signal.aborted; }

  /**
   * Gracefully pause after the current step finishes.
   * Unlike abort(), this lets the running step complete and saves a checkpoint.
   * After pause, use engine.executeFrom(ctx.checkpoint(), adapters, overrides) to resume.
   */
  pause() {
    if (this.status !== ExecutionStatus.Running) return;
    this.status = ExecutionStatus.Paused;
    this.emitEvent({
      type: RunEventType.WorkflowPaused,
      stepID: this.currentStepID,
      payload: { pausedAt: this.currentStepID }
    });
  }

  onEvent(fn) { this._eventListeners.push(fn); }
  emitEvent(event) {
    event.seq = ++this._eventSeq;
    event.ts = new Date().toISOString();
    event.runID = this.workflowID;
    for (const fn of this._eventListeners) { try { fn(event); } catch {} }
  }

  // Variable access (for expression evaluator)
  getVariable(name) {
    if (name.startsWith('$')) return this.variables[name];
    if (name.startsWith('_')) return this.localVars[name];
    if (name.startsWith('SYSVAR.')) return this.systemVars[name.slice(7)];
    return this.params[name];
  }
  setVariable(name, value) {
    if (name.startsWith('$')) {
      const old = this.variables[name];
      this.variables[name] = value;
      // Emit var_changed for pipeline variables (skip internal _locals)
      if (old !== value) {
        this.emitEvent({
          type: RunEventType.VarChanged,
          stepID: this.currentStepID,
          payload: { name, oldValue: old, newValue: value }
        });
      }
      return;
    }
    if (name.startsWith('_')) { this.localVars[name] = value; return; }
    throw new Error(`Cannot set read-only parameter: ${name}`);
  }

  /**
   * Snapshot current execution state — for external status queries.
   * Returns a plain JSON-serializable object.
   */
  snapshot() {
    return {
      runID: this.workflowID,
      status: this.status,
      currentStepID: this.currentStepID,
      elapsedMs: Date.now() - this.startTime,
      params: { ...this.params },
      variables: { ...this.variables },
      localVars: { ...this.localVars },
      artifacts: Object.keys(this.artifacts),
      paused: this.pauseState ? {
        nodeID: this.pauseState.nodeID,
        token: this.pauseState.token
      } : null,
      eventSeq: this._eventSeq,
      version: this.version
    };
  }

  /**
   * Create a serializable checkpoint of the entire execution state.
   * Can be persisted (JSON, file, DB) and used to resume via Engine.executeFrom().
   */
  checkpoint() {
    // Serialize _completedBranches: Map<string, Set<string>> → { parentID: [childIDs] }
    const completedBranches = {};
    for (const [parent, children] of this._completedBranches) {
      completedBranches[parent] = [...children];
    }
    // Serialize _loopProgress: Map<string, number> → { loopID: count }
    const loopProgress = {};
    for (const [loopID, count] of this._loopProgress) {
      loopProgress[loopID] = count;
    }
    return {
      _type: 'vl_workflow_checkpoint',
      _version: 2,
      workflowID: this.workflowID,
      currentStepID: this.currentStepID,
      status: this.status,
      params: structuredCloneJSON(this.params),
      paramTypes: { ...this.paramTypes },
      variables: structuredCloneJSON(this.variables),
      varTypes: { ...this.varTypes },
      localVars: structuredCloneJSON(this.localVars),
      artifacts: { ...this.artifacts },
      completedSteps: [...(this._completedSteps || [])],
      completedBranches,
      loopProgress,
      eventSeq: this._eventSeq,
      version: this.version,
      createdAt: new Date().toISOString()
    };
  }
}

// ─── Child Context (isolated local vars for parallel branches) ─
class ChildExecutionContext {
  constructor(parent) {
    this.parent = parent;
    this.localVars = {};
  }
  get workflowID() { return this.parent.workflowID; }
  get params() { return this.parent.params; }
  get paramTypes() { return this.parent.paramTypes; }
  get variables() { return this.parent.variables; }
  get varTypes() { return this.parent.varTypes; }
  get systemVars() { return this.parent.systemVars; }
  get artifacts() { return this.parent.artifacts; }
  get currentStepID() { return this.parent.currentStepID; }
  set currentStepID(v) { this.parent.currentStepID = v; }
  get status() { return this.parent.status; }
  set status(v) { this.parent.status = v; }
  get version() { return this.parent.version; }
  get serviceAdapter() { return this.parent.serviceAdapter; }
  get apiAdapter() { return this.parent.apiAdapter; }
  get componentAdapter() { return this.parent.componentAdapter; }
  get llmAdapter() { return this.parent.llmAdapter; }
  get fileAdapter() { return this.parent.fileAdapter; }
  get docAdapter() { return this.parent.docAdapter; }
  get pauseState() { return this.parent.pauseState; }
  set pauseState(v) { this.parent.pauseState = v; }
  get runParams() { return this.parent.runParams; }
  get signal() { return this.parent.signal; }
  get aborted() { return this.parent.aborted || Boolean(this._failFastSignal?.aborted); }
  get _completedSteps() { return this.parent._completedSteps; }
  get _completedBranches() { return this.parent._completedBranches; }
  get _loopProgress() { return this.parent._loopProgress; }
  onEvent(fn) { this.parent.onEvent(fn); }
  emitEvent(event) { this.parent.emitEvent(event); }

  getVariable(name) {
    if (name.startsWith('_')) return this.localVars[name];
    return this.parent.getVariable(name);
  }
  setVariable(name, value) {
    if (name.startsWith('_')) { this.localVars[name] = value; return; }
    this.parent.setVariable(name, value);
  }
}

/** Deep-clone via JSON round-trip (safe for checkpoint serialization). */
function structuredCloneJSON(obj) {
  if (obj === null || obj === undefined) return obj;
  try { return JSON.parse(JSON.stringify(obj)); } catch { return obj; }
}

module.exports = {
  WorkflowType, StepType, getStepType, resolveStepBehavior, STEP_ALIAS,
  ExecutionStatus, WriteMode, LoopMode,
  ParallelErrorStrategy, RunEventType, SUPPORTED_VERSIONS, isV310OrLater,
  RESERVED_NEXT_KEYWORDS,
  LLMError, buildErrorMap, ExecutionContext, ChildExecutionContext, structuredCloneJSON
};
