#!/usr/bin/env node
// Backfill component metadata 進 registry index
// SDD: matrix/arcrun/.agents/specs/component-registry-canon/design.md Phase 1
//
// 用法：
//   node scripts/backfill-index.mjs --dry-run   # 看會送什麼
//   node scripts/backfill-index.mjs              # 真的灌
//
// 流程：
//   1. 掃 ../components/*/component.contract.yaml
//   2. 解析 YAML（用 zero-dep 簡易 parser，contract 是 well-formed YAML）
//   3. 對每個 contract POST registry.arcrun.dev/components/index-only
//   4. 印 success / already_indexed / fail 統計

import { readdirSync, readFileSync, statSync } from 'node:fs';
import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const COMPONENTS_DIR = join(__dirname, '..', 'components');
const REGISTRY_URL = process.env.REGISTRY_URL ?? 'https://registry.arcrun.dev';
const DRY_RUN = process.argv.includes('--dry-run');

// YAML 是 well-formed contract.yaml，用 js-yaml 解析最穩
async function parseYaml(text) {
  const { load } = await import('js-yaml');
  return load(text);
}

function listComponents() {
  return readdirSync(COMPONENTS_DIR)
    .filter((name) => {
      const p = join(COMPONENTS_DIR, name);
      return statSync(p).isDirectory();
    })
    .sort();
}

async function readContract(name) {
  const path = join(COMPONENTS_DIR, name, 'component.contract.yaml');
  const text = readFileSync(path, 'utf-8');
  return parseYaml(text);
}

async function postIndexOnly(contract) {
  const res = await fetch(`${REGISTRY_URL}/components/index-only`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ contract }),
  });
  const body = await res.text();
  let parsed;
  try {
    parsed = JSON.parse(body);
  } catch {
    parsed = { raw: body };
  }
  return { status: res.status, body: parsed };
}

async function main() {
  console.log('=== arcrun Component Registry Backfill ===');
  console.log(`Registry: ${REGISTRY_URL}`);
  console.log(`Mode: ${DRY_RUN ? 'DRY RUN' : 'LIVE'}`);
  console.log();

  const names = listComponents();
  console.log(`Found ${names.length} components in ${COMPONENTS_DIR}`);
  console.log();

  const stats = { created: 0, already: 0, fail: 0 };

  for (const name of names) {
    let contract;
    try {
      contract = await readContract(name);
    } catch (e) {
      console.log(`  ✗ ${name.padEnd(28)} READ FAIL: ${e.message}`);
      stats.fail++;
      continue;
    }

    const cid = contract.canonical_id ?? '(no canonical_id)';
    const ver = contract.version ?? '(no version)';

    if (DRY_RUN) {
      console.log(`  → ${name.padEnd(28)} ${cid} ${ver}`);
      continue;
    }

    try {
      const { status, body } = await postIndexOnly(contract);
      if (status === 200 && body.already_indexed) {
        console.log(`  = ${name.padEnd(28)} ${cid} ${ver}  [already]`);
        stats.already++;
      } else if (status === 201) {
        console.log(`  ✓ ${name.padEnd(28)} ${cid} ${ver}  [${body.component_hash_id}]`);
        stats.created++;
      } else {
        console.log(`  ✗ ${name.padEnd(28)} ${cid} ${ver}  HTTP ${status}: ${JSON.stringify(body).slice(0, 200)}`);
        stats.fail++;
      }
    } catch (e) {
      console.log(`  ✗ ${name.padEnd(28)} POST FAIL: ${e.message}`);
      stats.fail++;
    }
  }

  console.log();
  console.log('=== Summary ===');
  console.log(`Created:        ${stats.created}`);
  console.log(`Already indexed: ${stats.already}`);
  console.log(`Failed:         ${stats.fail}`);
  process.exit(stats.fail > 0 ? 1 : 0);
}

main().catch((e) => {
  console.error('Fatal:', e);
  process.exit(1);
});
