import { sanitizeWorkflowOutputForDisplay } from './workflowFinalOutput';

export interface WorkflowExecutionTrace {
  id: string;
  outcome: 'matched' | 'skipped';
  targetNodeName: string;
}

export type WorkflowExecutionStepStatus =
  | 'cancelled'
  | 'completed'
  | 'failed'
  | 'running'
  | 'waiting';

export interface WorkflowExecutionStepView {
  attemptKey?: string;
  duration?: number;
  endTime?: number;
  error?: string;
  hasInput: boolean;
  hasOutput: boolean;
  input?: unknown;
  key: string;
  nodeClass?: string;
  nodeId: string;
  nodeName: string;
  output?: unknown;
  startTime?: number;
  status: WorkflowExecutionStepStatus;
  traces: WorkflowExecutionTrace[];
}

interface WorkflowExecutionEvent {
  data?: Record<string, any>;
  eventId: string;
  type: string;
}

/**
 * 将实时工作流事件归并为稳定的节点执行步骤。
 */
export function reduceWorkflowExecutionSteps(
  current: WorkflowExecutionStepView[],
  event: WorkflowExecutionEvent,
  now = Date.now(),
): WorkflowExecutionStepView[] {
  if (
    event.type !== 'node_started' &&
    event.type !== 'node_finished' &&
    event.type !== 'node_trace'
  ) {
    return current;
  }
  const data = event.data || {};
  const attemptKey = textValue(data.attemptKey);
  const nodeId = textValue(data.nodeId);
  const stepIndex = findStepIndex(current, attemptKey, nodeId);

  if (event.type === 'node_started') {
    const startTime = numberValue(data.startedAt) ?? now;
    const nextStep: WorkflowExecutionStepView = {
      attemptKey: attemptKey || undefined,
      hasInput: hasOwn(data, 'input'),
      hasOutput: false,
      input: data.input,
      key: attemptKey || `${nodeId || 'node'}:${event.eventId}`,
      nodeClass: textValue(data.nodeClass) || undefined,
      nodeId,
      nodeName: textValue(data.nodeName) || nodeId || '工作流节点',
      startTime,
      status: 'running',
      traces: [],
    };
    if (stepIndex === -1) {
      return [...current, nextStep];
    }
    const existingStep = current[stepIndex];
    if (!existingStep) {
      return [...current, nextStep];
    }
    const next = [...current];
    next[stepIndex] = {
      ...existingStep,
      ...nextStep,
      traces: existingStep.traces,
    };
    return next;
  }

  const existingStep = stepIndex === -1 ? undefined : current[stepIndex];
  const baseStep =
    existingStep === undefined
      ? createFallbackStep(event, data, attemptKey, nodeId, now)
      : existingStep;
  let updated: WorkflowExecutionStepView;
  if (event.type === 'node_trace') {
    const targetNodeName =
      textValue(data.targetNodeName) ||
      textValue(data.targetNodeId) ||
      '后续节点';
    const outcome = data.outcome === 'matched' ? 'matched' : 'skipped';
    const trace: WorkflowExecutionTrace = {
      id: event.eventId,
      outcome,
      targetNodeName,
    };
    updated = {
      ...baseStep,
      traces: baseStep.traces.some((item) => item.id === trace.id)
        ? baseStep.traces
        : [...baseStep.traces, trace],
    };
  } else {
    const endTime = numberValue(data.finishedAt) ?? now;
    const startTime = baseStep.startTime;
    updated = {
      ...baseStep,
      duration:
        startTime === undefined ? undefined : Math.max(0, endTime - startTime),
      endTime,
      error: textValue(data.error) || undefined,
      hasOutput: hasOwn(data, 'output'),
      output: data.output,
      status: resolveLiveStatus(data.status, data.error),
    };
  }

  if (stepIndex === -1) {
    return [...current, updated];
  }
  const next = [...current];
  next[stepIndex] = updated;
  return next;
}

/**
 * 将持久化步骤转换为运行详情统一视图。
 */
export function hydrateWorkflowExecutionSteps(
  steps: unknown,
): WorkflowExecutionStepView[] {
  if (!Array.isArray(steps)) {
    return [];
  }
  return steps.map((step: Record<string, any>, index) => ({
    attemptKey: textValue(step.attemptKey) || undefined,
    duration: numberValue(step.execTime),
    endTime: timeValue(step.endTime),
    error: textValue(step.errorInfo) || undefined,
    hasInput: step.input !== undefined && step.input !== null,
    hasOutput: step.output !== undefined && step.output !== null,
    input: parseExecutionValue(step.input),
    key:
      textValue(step.attemptKey) ||
      textValue(step.id) ||
      `${textValue(step.nodeId) || 'node'}:${index}`,
    nodeId: textValue(step.nodeId),
    nodeName:
      textValue(step.nodeName) || textValue(step.nodeId) || '工作流节点',
    output: parseExecutionValue(step.output),
    startTime: timeValue(step.startTime),
    status: resolvePersistedStatus(step.status),
    traces: [],
  }));
}

/**
 * 将仍在执行的步骤收口为工作流终态。
 */
export function finalizeWorkflowExecutionSteps(
  steps: WorkflowExecutionStepView[],
  status: 'cancelled' | 'completed' | 'failed',
  now = Date.now(),
): WorkflowExecutionStepView[] {
  let changed = false;
  const next = steps.map((step) => {
    if (step.status !== 'running' && step.status !== 'waiting') {
      return step;
    }
    changed = true;
    return {
      ...step,
      duration:
        step.startTime === undefined
          ? step.duration
          : Math.max(0, now - step.startTime),
      endTime: now,
      status,
    };
  });
  return changed ? next : steps;
}

/**
 * 格式化节点输入或输出。
 *
 * @param value 节点输入或输出
 * @param sanitizeThinking 是否移除旧思考标签内容块
 */
export function formatExecutionValue(value: unknown, sanitizeThinking = false) {
  const displayValue = sanitizeThinking
    ? sanitizeWorkflowOutputForDisplay(value)
    : value;
  if (typeof displayValue === 'string') {
    return displayValue;
  }
  try {
    return JSON.stringify(displayValue ?? null, null, 2);
  } catch {
    return String(displayValue ?? '');
  }
}

function createFallbackStep(
  event: WorkflowExecutionEvent,
  data: Record<string, any>,
  attemptKey: string,
  nodeId: string,
  now: number,
): WorkflowExecutionStepView {
  return {
    attemptKey: attemptKey || undefined,
    hasInput: false,
    hasOutput: false,
    key: attemptKey || `${nodeId || 'node'}:${event.eventId}`,
    nodeId,
    nodeName: textValue(data.nodeName) || nodeId || '工作流节点',
    startTime: now,
    status: 'running',
    traces: [],
  };
}

function findStepIndex(
  steps: WorkflowExecutionStepView[],
  attemptKey: string,
  nodeId: string,
) {
  for (let index = steps.length - 1; index >= 0; index -= 1) {
    const step = steps[index];
    if (!step) {
      continue;
    }
    if (attemptKey && step.attemptKey === attemptKey) {
      return index;
    }
    if (!attemptKey && nodeId && step.nodeId === nodeId) {
      return index;
    }
  }
  return -1;
}

function parseExecutionValue(value: unknown) {
  if (typeof value !== 'string') {
    return value;
  }
  const text = value.trim();
  if (!text) {
    return '';
  }
  try {
    return JSON.parse(text);
  } catch {
    return value;
  }
}

function resolveLiveStatus(
  status: unknown,
  error: unknown,
): WorkflowExecutionStepStatus {
  const normalized = textValue(status).toUpperCase();
  if (error || normalized === 'ERROR' || normalized === 'FAILED') {
    return 'failed';
  }
  if (normalized === 'SUSPEND') {
    return 'waiting';
  }
  return normalized === 'RUNNING' ? 'running' : 'completed';
}

function resolvePersistedStatus(status: unknown): WorkflowExecutionStepStatus {
  switch (String(status)) {
    case '1': {
      return 'running';
    }
    case '5': {
      return 'waiting';
    }
    case '10':
    case '21': {
      return 'failed';
    }
    default: {
      return 'completed';
    }
  }
}

function hasOwn(value: object, key: string) {
  return Object.prototype.hasOwnProperty.call(value, key);
}

function numberValue(value: unknown) {
  if (typeof value === 'number' && Number.isFinite(value)) {
    return value;
  }
  return undefined;
}

function timeValue(value: unknown) {
  const direct = numberValue(value);
  if (direct !== undefined) {
    return direct;
  }
  if (typeof value !== 'string' || !value) {
    return undefined;
  }
  const timestamp = Date.parse(value);
  return Number.isNaN(timestamp) ? undefined : timestamp;
}

function textValue(value: unknown) {
  return value === undefined || value === null ? '' : String(value);
}
