/**
 * workflow.yaml 解析與三元組驗證
 */
import yaml from 'js-yaml';
import { readFileSync } from 'node:fs';

export interface WorkflowYaml {
  name: string;
  description?: string;
  flow: string[];
  config?: Record<string, Record<string, unknown>>;
}

export interface ParsedTriplet {
  subject: string;
  relation: string;
  object: string;
}

/** 合法關係詞（拒絕 PIPE）*/
const VALID_RELATIONS = new Set([
  '完成後', '失敗時', '對每個', '條件滿足時',
  'ON_SUCCESS', 'ON_FAIL', 'FOREACH', 'IF', 'ON_CLICK', 'CALLS_SUBFLOW',
]);

const BANNED_RELATIONS = new Set(['PIPE']);

export function loadWorkflowYaml(filePath: string): WorkflowYaml {
  const raw = readFileSync(filePath, 'utf8');
  const doc = yaml.load(raw) as WorkflowYaml;

  if (!doc.name) throw new Error('workflow.yaml 缺少 name 欄位');
  if (!Array.isArray(doc.flow) || doc.flow.length === 0) {
    throw new Error('workflow.yaml 的 flow 欄位必須為非空陣列');
  }

  return doc;
}

export function parseTriplets(flow: string[]): ParsedTriplet[] {
  const triplets: ParsedTriplet[] = [];

  for (const line of flow) {
    const parts = line.split('>>').map(s => s.trim());
    if (parts.length !== 3) {
      throw new Error(
        `三元組格式錯誤：「${line}」\n` +
        `正確格式：「A >> 關係詞 >> B」`
      );
    }
    const [subject, relation, object] = parts;
    triplets.push({ subject, relation, object });
  }

  return triplets;
}

export function validateRelations(triplets: ParsedTriplet[]): void {
  for (const t of triplets) {
    if (BANNED_RELATIONS.has(t.relation)) {
      throw new Error(
        `不允許使用關係詞「${t.relation}」。\n` +
        `「PIPE」已棄用，請改用「完成後」或「ON_SUCCESS」。`
      );
    }
    // 容許 FOREACH iterator 命名變體：「對每個 paragraph」/「FOREACH item」
    // graph-builder.ts 已支援這個 regex（commit e8fca33 2026-05-07）
    const foreachMatch = t.relation.match(/^(?:對每個|FOREACH)\s+\w+$/i);
    if (foreachMatch) continue;

    if (!VALID_RELATIONS.has(t.relation)) {
      throw new Error(
        `未知關係詞「${t.relation}」。\n` +
        `合法關係詞：${[...VALID_RELATIONS].join('、')}\n` +
        `（FOREACH 支援 iterator 命名：「對每個 X」/「FOREACH X」）`
      );
    }
  }
}

export function getNodeNames(triplets: ParsedTriplet[]): string[] {
  const nodes = new Set<string>();
  for (const t of triplets) {
    nodes.add(t.subject);
    nodes.add(t.object);
  }
  return [...nodes];
}
