/**
 * acr recipe push <file>   — 上傳 recipe YAML 到 arcrun.dev
 * acr recipe list          — 列出已上傳的 recipe
 * acr recipe delete <id>   — 刪除 recipe（canonical_id 或 rec_hash）
 */
import chalk from 'chalk';
import ora from 'ora';
import { readFileSync, existsSync } from 'node:fs';
import { loadConfig, getCypherExecutorUrl, DEFAULT_PUBLIC_LIBRARY_URL } from '../lib/config.js';
import yaml from 'js-yaml';

interface RecipeYaml {
  canonical_id?: string;
  display_name?: string;
  description?: string;
  endpoint?: string;
  method?: string;
  headers?: Record<string, string>;
  body?: Record<string, unknown>;
  credentials_required?: Array<{ key: string; inject_as: string }>;
}

interface RecipeDefinition {
  uuid?: string;            // UUID 身份模型（kbdb-base §7.5.5）
  author?: string;
  derived_from?: string;
  canonical_id: string;
  hash_id: string;
  display_name?: string;
  description?: string;
  endpoint: string;
  method?: string;
  credentials_required?: Array<{ key: string; inject_as: string }>;
  created_at: number;
  updated_at: number;
}

export async function cmdRecipePush(filePath: string): Promise<void> {
  const config = loadConfig();

  if (!config.api_key) {
    console.error(chalk.red('缺少 API Key，請先執行 acr init 取得 API Key'));
    process.exit(1);
  }

  if (!existsSync(filePath)) {
    console.error(chalk.red(`找不到檔案：${filePath}`));
    process.exit(1);
  }

  // 讀取並解析 YAML
  let recipe: RecipeYaml;
  try {
    const raw = readFileSync(filePath, 'utf8');
    recipe = yaml.load(raw) as RecipeYaml;
  } catch (e) {
    console.error(chalk.red(`YAML 解析失敗：${e instanceof Error ? e.message : e}`));
    process.exit(1);
  }

  if (!recipe.canonical_id) {
    console.error(chalk.red('recipe YAML 缺少 canonical_id 欄位'));
    process.exit(1);
  }
  if (!recipe.endpoint) {
    console.error(chalk.red('recipe YAML 缺少 endpoint 欄位'));
    process.exit(1);
  }

  const executorUrl = getCypherExecutorUrl(config);

  // 暴露 consent 閘已移除（leo 2026-06-29，Arcrun#13）：recipe push 不再需要人類確認。

  const spinner = ora(`上傳 recipe "${recipe.canonical_id}"`).start();

  try {
    const res = await fetch(`${executorUrl}/recipes`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'X-Arcrun-API-Key': config.api_key,
      },
      body: JSON.stringify(recipe),
    });

    const data = await res.json() as { success: boolean; recipe?: RecipeDefinition; error?: string };

    if (!data.success || !data.recipe) {
      spinner.fail(chalk.red(`上傳失敗：${data.error ?? '未知錯誤'}`));
      process.exit(1);
    }

    spinner.succeed(chalk.green(`✓ recipe "${data.recipe.canonical_id}" 上傳成功`));
    console.log(`\n  Hash ID：${chalk.cyan(data.recipe.hash_id)}  （穩定引用，不受改名影響）`);
    console.log(`  Endpoint：${chalk.gray(data.recipe.endpoint)}`);

    // 打通檢查（SDD recipe-push-gatekeeping §1.2）：recipe 是「指向外部 API 的指針」，
    // 正確性一半在「打不打得通」（DECISIONS §1 recipe 驗收 = 2xx）。
    // self-hosted 是提醒級：不硬擋、誠實標原因（缺 credential 打不到 2xx 就誠實說，不假綠 — mindset §7）。
    await probeRecipeEndpoint(recipe);

    console.log(chalk.bold('\n  在 workflow config 中使用：\n'));
    console.log(chalk.cyan(`  config:`));
    console.log(chalk.cyan(`    my_node:`));
    console.log(chalk.cyan(`      component: ${data.recipe.canonical_id}  # 或用 hash: ${data.recipe.hash_id}`));
    if (data.recipe.credentials_required?.length) {
      console.log(chalk.yellow(`\n  此 recipe 需要 credentials：${data.recipe.credentials_required.map(c => c.key).join(', ')}`));
      console.log(chalk.gray('  執行 acr creds push 上傳 token'));
    }
    console.log('');
  } catch (e) {
    spinner.fail(chalk.red(`網路錯誤：${e instanceof Error ? e.message : e}`));
    process.exit(1);
  }
}

/**
 * 打通檢查：push 時對 recipe endpoint 實打一次，回報是否 2xx。
 *
 * 提醒級（self-hosted）：只回報、不硬擋（用戶可能就是要先 push 再設 credential）。
 * 誠實（mindset §7）：
 *   - endpoint 含未填模板（{{_path}} / {{auth.x}} 等）→ 執行期才有值，push 時無法驗，誠實說明。
 *   - 打不到 2xx → 誠實標 HTTP status（如 401 多半是缺 credential），不假裝成功。
 *   - arcrun 不做授權判斷：401/403 是對方服務裁決，不是 recipe 的 bug（DECISIONS / mindset §3）。
 */
async function probeRecipeEndpoint(recipe: RecipeYaml): Promise<void> {
  const endpoint = recipe.endpoint ?? '';
  if (/\{\{.*?\}\}/.test(endpoint)) {
    console.log(chalk.gray('\n  打通檢查：endpoint 含執行期變數（{{...}}），push 時無法預打。'));
    console.log(chalk.gray('  實際是否打通待 acr run 時才知（recipe 驗收標準 = 執行回 2xx）。'));
    return;
  }

  process.stdout.write(chalk.gray('\n  打通檢查（實打 endpoint）...'));
  try {
    const method = (recipe.method ?? 'POST').toUpperCase();
    const res = await fetch(endpoint, {
      method,
      headers: recipe.headers,
      // 不帶 credential（push 端沒有明文）→ 打不通多半是缺 auth，下面誠實標
      ...(method !== 'GET' && method !== 'HEAD'
        ? { body: JSON.stringify(recipe.body ?? {}) }
        : {}),
      signal: AbortSignal.timeout(10_000),
    });
    if (res.ok) {
      console.log(chalk.green(` ✓ HTTP ${res.status}（打通）`));
    } else if (res.status === 401 || res.status === 403) {
      console.log(chalk.yellow(` ⚠ HTTP ${res.status}`));
      console.log(chalk.gray('  未驗收：多半是缺 credential（過認證後才會 2xx）。先 acr creds push 對應 token。'));
      console.log(chalk.gray('  註：401/403 是對方服務在行使授權，不是 recipe 的 bug。'));
    } else {
      console.log(chalk.yellow(` ⚠ HTTP ${res.status}（未打通）`));
      console.log(chalk.gray('  recipe 已上傳，但 endpoint 目前未回 2xx。請確認 endpoint / method 正確。'));
    }
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e);
    console.log(chalk.yellow(` ⚠ 無法連線`));
    console.log(chalk.gray(`  ${msg.slice(0, 120)}（recipe 已上傳；連線問題不擋 push）`));
  }
}

export async function cmdRecipeList(): Promise<void> {
  const config = loadConfig();
  const executorUrl = getCypherExecutorUrl(config);
  const spinner = ora('取得 recipe 清單').start();

  try {
    const headers: Record<string, string> = {};
    if (config.api_key) headers['X-Arcrun-API-Key'] = config.api_key;

    const res = await fetch(`${executorUrl}/recipes`, { headers });
    const data = await res.json() as { success: boolean; recipes?: RecipeDefinition[]; error?: string };

    spinner.stop();

    if (!data.success) {
      console.error(chalk.red(`錯誤：${data.error}`));
      process.exit(1);
    }

    const recipes = data.recipes ?? [];
    if (recipes.length === 0) {
      console.log(chalk.gray('\n  尚無 recipe。執行 acr recipe push <file> 上傳。\n'));
      return;
    }

    console.log(chalk.bold(`\n  arcrun recipes（${recipes.length} 個）\n`));
    for (const r of recipes) {
      console.log(`  • ${chalk.cyan(r.canonical_id.padEnd(20))} ${chalk.gray(r.hash_id)}  ${r.display_name ?? ''}`);
      console.log(`    ${chalk.gray(r.endpoint)}`);
      if (r.credentials_required?.length) {
        console.log(`    ${chalk.yellow('🔑 需要：' + r.credentials_required.map(c => c.key).join(', '))}`);
      }
    }
    console.log('');
  } catch (e) {
    spinner.fail(chalk.red(`網路錯誤：${e instanceof Error ? e.message : e}`));
    process.exit(1);
  }
}

export async function cmdRecipeDelete(id: string): Promise<void> {
  const config = loadConfig();

  if (!config.api_key) {
    console.error(chalk.red('缺少 API Key，請先執行 acr init'));
    process.exit(1);
  }

  const executorUrl = getCypherExecutorUrl(config);
  const spinner = ora(`刪除 recipe "${id}"`).start();

  try {
    const res = await fetch(`${executorUrl}/recipes/${id}`, {
      method: 'DELETE',
      headers: { 'X-Arcrun-API-Key': config.api_key },
    });

    const data = await res.json() as { success: boolean; deleted?: string; error?: string };

    if (!data.success) {
      spinner.fail(chalk.red(`刪除失敗：${data.error ?? '未知錯誤'}`));
      process.exit(1);
    }

    spinner.succeed(chalk.green(`✓ recipe "${data.deleted}" 已刪除`));
  } catch (e) {
    spinner.fail(chalk.red(`網路錯誤：${e instanceof Error ? e.message : e}`));
    process.exit(1);
  }
}

// ── 公庫互動（kbdb-base §7.5，薄殼：只呼叫 API + 格式化，無業務邏輯）─────────────────

interface PublicRecipeSummary {
  uuid?: string;
  canonical_id: string;
  author?: string;
  display_name?: string;
  description?: string;
  market_stat?: { success_count: number; failure_count: number } | null;
}

/** acr recipe search <q> — 搜尋公庫（GET /public-recipes?q=）。落空回創作引導（§7.5.6）。*/
export async function cmdRecipeSearch(query: string): Promise<void> {
  const spinner = ora(`搜尋公庫「${query}」`).start();
  try {
    const url = `${DEFAULT_PUBLIC_LIBRARY_URL}/public-recipes?q=${encodeURIComponent(query)}`;
    const res = await fetch(url);
    const data = await res.json() as
      | { found: true; recipes: PublicRecipeSummary[]; count: number }
      | { found: false; query: string; hint: string };
    spinner.stop();

    if (!data.found) {
      console.log(chalk.yellow(`\n  公庫無符合「${query}」的 recipe。`));
      console.log(chalk.gray(`  ${data.hint}`));
      console.log(chalk.gray('  做一個：建 recipe YAML → acr recipe push（私庫）→ acr recipe submit-p（投稿成為作者）。\n'));
      return;
    }

    console.log(chalk.bold(`\n  公庫 recipes（${data.count} 個，同名可多作者）\n`));
    for (const r of data.recipes) {
      const s = r.market_stat;
      const stat = s ? chalk.gray(`  ✓${s.success_count}/✗${s.failure_count}`) : chalk.gray('  （無市場數據）');
      console.log(`  • ${chalk.cyan(r.canonical_id.padEnd(20))} ${chalk.magenta('@' + (r.author ?? '?'))}${stat}  ${r.display_name ?? ''}`);
      if (r.description) console.log(`    ${chalk.gray(r.description)}`);
    }
    console.log(chalk.gray('\n  取用：acr recipe pull <canonical_id> [--author=<name>]\n'));
  } catch (e) {
    spinner.fail(chalk.red(`網路錯誤：${e instanceof Error ? e.message : e}`));
    process.exit(1);
  }
}

/** acr recipe pull <canonical_id> [--author] — 從公庫取一份 recipe 寫進自己私庫。*/
export async function cmdRecipePull(canonicalId: string, author?: string): Promise<void> {
  const config = loadConfig();
  if (!config.api_key) {
    console.error(chalk.red('缺少 API Key，請先執行 acr init'));
    process.exit(1);
  }
  const spinner = ora(`從公庫取 recipe「${canonicalId}」${author ? `（@${author}）` : ''}`).start();
  try {
    // 1. 從公庫取全文（不指定 author → 公庫回市場最佳版本）。
    const q = author ? `?author=${encodeURIComponent(author)}` : '';
    const pubRes = await fetch(`${DEFAULT_PUBLIC_LIBRARY_URL}/public-recipes/${encodeURIComponent(canonicalId)}${q}`);
    const pub = await pubRes.json() as
      | { found: true; recipe: RecipeDefinition & { uuid?: string; author?: string }; market_stat?: unknown }
      | { found: false; canonical_id: string; hint: string };

    if (!pub.found) {
      spinner.stop();
      console.log(chalk.yellow(`\n  公庫無 recipe「${canonicalId}」。`));
      console.log(chalk.gray(`  ${pub.hint}\n`));
      return;
    }

    // 2. 寫進自己私庫（POST /recipes，帶 derived_from 溯源）。
    const r = pub.recipe;
    const executorUrl = getCypherExecutorUrl(config);
    const installRes = await fetch(`${executorUrl}/recipes`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json', 'X-Arcrun-API-Key': config.api_key },
      body: JSON.stringify({
        ...r,
        derived_from: r.uuid,       // 溯源：私庫這份來自公庫哪個 uuid
      }),
    });
    const inst = await installRes.json() as { success: boolean; recipe?: RecipeDefinition; error?: string };
    if (!inst.success) {
      spinner.fail(chalk.red(`寫入私庫失敗：${inst.error ?? '未知錯誤'}`));
      process.exit(1);
    }
    spinner.succeed(chalk.green(`✓ recipe「${canonicalId}」${author ? `（@${author}）` : ''} 已拉進私庫`));
    console.log(chalk.gray(`  在 workflow 用 component: ${canonicalId}\n`));
  } catch (e) {
    spinner.fail(chalk.red(`網路錯誤：${e instanceof Error ? e.message : e}`));
    process.exit(1);
  }
}

/** acr recipe submit-p <canonical_id> — 把私庫某 recipe 投稿到公庫（新增作者版本，需暴露同意）。*/
export async function cmdRecipeSubmitP(canonicalId: string, author?: string): Promise<void> {
  const config = loadConfig();
  if (!config.api_key) {
    console.error(chalk.red('缺少 API Key，請先執行 acr init'));
    process.exit(1);
  }
  const executorUrl = getCypherExecutorUrl(config);

  // 1. 從私庫取這份 recipe 全文。
  const myRes = await fetch(`${executorUrl}/recipes/${encodeURIComponent(canonicalId)}`, {
    headers: { 'X-Arcrun-API-Key': config.api_key },
  });
  const my = await myRes.json() as { success: boolean; recipe?: RecipeDefinition & { uuid?: string }; error?: string };
  if (!my.success || !my.recipe) {
    console.error(chalk.red(`私庫找不到 recipe「${canonicalId}」：${my.error ?? ''}`));
    process.exit(1);
  }

  // 暴露 consent 閘已移除（leo 2026-06-29，Arcrun#13）：投稿公庫不再需要人類確認。

  const spinner = ora(`投稿 recipe「${canonicalId}」到公庫`).start();
  try {
    const res = await fetch(`${DEFAULT_PUBLIC_LIBRARY_URL}/recipes/submit`, {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify({
        ...my.recipe,
        author: author ?? my.recipe.author,
        derived_from: my.recipe.derived_from ?? my.recipe.uuid,
        submitter: author ?? config.api_key,
      }),
    });
    const data = await res.json() as { success: boolean; recipe?: { uuid?: string; author?: string }; error?: string };
    if (!data.success) {
      spinner.fail(chalk.red(`投稿失敗：${data.error ?? '未知錯誤'}`));
      process.exit(1);
    }
    spinner.succeed(chalk.green(`✓ recipe「${canonicalId}」已投稿公庫（新增作者版本 @${data.recipe?.author ?? '?'}）`));
    console.log(chalk.gray('  別人能搜到並 pull；市場數據累積後決定它被不被選用。\n'));
  } catch (e) {
    spinner.fail(chalk.red(`網路錯誤：${e instanceof Error ? e.message : e}`));
    process.exit(1);
  }
}
