/**
 * acr validate <workflow.yaml>
 * 在執行前驗證 YAML 格式、關係詞合法性、零件是否存在、credentials 是否已上傳
 */
import chalk from 'chalk';
import { loadConfig, getCypherExecutorUrl } from '../lib/config.js';
import { loadWorkflowYaml, parseTriplets, validateRelations, getNodeNames } from '../lib/yaml-parser.js';
import { CfKvClient } from '../lib/cf-api.js';

export async function cmdValidate(filePath: string, options: { offline?: boolean } = {}): Promise<void> {
  const config = loadConfig();
  let allPassed = true;

  const check = (label: string, ok: boolean, detail?: string) => {
    const icon = ok ? chalk.green('✓') : chalk.red('✗');
    console.log(`  ${icon} ${label}${detail ? `  ${chalk.gray(detail)}` : ''}`);
    if (!ok) allPassed = false;
  };

  console.log(chalk.bold(`\n  驗證 ${filePath}\n`));

  // 1. YAML 格式
  let workflow;
  try {
    workflow = loadWorkflowYaml(filePath);
    check('YAML 格式正確', true, `name=${workflow.name}`);
  } catch (e) {
    check('YAML 格式', false, e instanceof Error ? e.message : String(e));
    process.exit(1);
  }

  // 2. 三元組解析
  let triplets;
  try {
    triplets = parseTriplets(workflow.flow);
    check('三元組解析', true, `${triplets.length} 條`);
  } catch (e) {
    check('三元組解析', false, e instanceof Error ? e.message : String(e));
    process.exit(1);
  }

  // 3. 關係詞驗證（不允許 PIPE）
  try {
    validateRelations(triplets);
    check('關係詞合法性', true);
  } catch (e) {
    check('關係詞合法性', false, e instanceof Error ? e.message : String(e));
    allPassed = false;
  }

  // 4. 所有節點在 config 中有對應
  // 規則：除了名稱為 "input" 的保留字節點，其餘所有節點都必須有 config
  // 注意：不能用「只出現在 subject 或只出現在 object」來判斷是否需要 config，
  //       因為像 a >> b >> c 中 a、b、c 都可能需要 config
  const nodeNames = getNodeNames(triplets);
  const RESERVED_NODES = new Set(['input']);  // 保留字節點不需要 config
  const componentNodes = nodeNames.filter(n => !RESERVED_NODES.has(n));

  const missingConfigs = componentNodes.filter(n => !(workflow.config ?? {})[n]);
  if (missingConfigs.length > 0) {
    check('config 完整性', false, `缺少 config：${missingConfigs.join(', ')}`);
    allPassed = false;
  } else {
    check('config 完整性', true, `${componentNodes.length} 個節點均有 config`);
  }

  // 5. 零件存在性（--offline 時跳過）
  if (options.offline || config.mode === 'local') {
    check('零件存在性', true, '離線模式，跳過遠端檢查');
  } else {
    const executorUrl = getCypherExecutorUrl(config);
    try {
      const headers: Record<string, string> = { 'Content-Type': 'application/json' };
      if (config.api_key) headers['X-Arcrun-API-Key'] = config.api_key;

      const res = await fetch(`${executorUrl}/cypher/search`, {
        method: 'POST',
        headers,
        body: JSON.stringify({ triplets: workflow.flow }),
      });

      if (res.ok) {
        const data = await res.json() as { missing: string[] };
        if (data.missing.length > 0) {
          check('零件存在性', false, `registry 中找不到零件：${data.missing.join(', ')}`);
          allPassed = false;
        } else {
          check('零件存在性', true, '所有零件均已在 registry');
        }
      } else {
        check('零件存在性', false, `無法連線 ${executorUrl}（加 --offline 跳過此檢查）`);
      }
    } catch {
      check('零件存在性', false, `無法連線 ${executorUrl}（加 --offline 跳過此檢查）`);
    }
  }

  // 6. Credentials 上傳檢查（--offline 或 local 模式時跳過）
  if (options.offline || config.mode === 'local') {
    // 離線模式略過
  } else if (config.cloudflare_account_id && config.cf_api_token) {
    const namespaceId = config.mode === 'standard'
      ? config.user_kv_namespace_id
      : config.credentials_kv_namespace_id;

    if (namespaceId) {
      const kv = new CfKvClient({
        accountId: config.cloudflare_account_id,
        namespaceId,
        apiToken: config.cf_api_token,
      });

      try {
        const kvKeys = await kv.list('cred:');
        const uploadedCreds = new Set(kvKeys.map(k => k.name.replace('cred:', '')));
        const usedCreds = extractCredentialRefs(workflow.config ?? {});
        const missingCreds = usedCreds.filter(k => !uploadedCreds.has(k));

        if (missingCreds.length > 0) {
          check('Credentials 上傳', false, `未上傳：${missingCreds.join(', ')}（執行 acr creds push）`);
          allPassed = false;
        } else {
          check('Credentials 上傳', true, `已上傳 ${uploadedCreds.size} 個 credential`);
        }
      } catch {
        check('Credentials 上傳', false, 'KV 查詢失敗，跳過驗證');
      }
    }
  }

  console.log('');
  if (allPassed) {
    console.log(chalk.green.bold('  ✓ 驗證通過\n'));
  } else {
    console.log(chalk.red.bold('  ✗ 驗證未通過，請修正上方錯誤\n'));
    process.exit(1);
  }
}

/** 從 workflow config 中提取可能的 credential key 引用（模板 {{creds.xxx}}）*/
function extractCredentialRefs(config: Record<string, Record<string, unknown>>): string[] {
  const refs = new Set<string>();
  const jsonStr = JSON.stringify(config);
  const matches = jsonStr.matchAll(/\{\{creds\.([^}]+)\}\}/g);
  for (const m of matches) {
    refs.add(m[1]);
  }
  return [...refs];
}
