/**
 * A2A 終端化ヘルパ — executor と reconciler で共有する pure ロジック。
 *
 * - enumerateOutputArtifacts: workspace/output/ 直下のファイルを列挙し、
 *   インライン可能な場合はテキスト化、そうでない場合は descriptive メッセージを返す。
 * - finalizeStatusFromJob: MAESTRO ジョブの terminal status を A2A state に写像する。
 */

import * as fs from 'fs';
import * as path from 'path';
import * as crypto from 'crypto';
import { logger } from '../../logger.js';

/** Fix 3: artifact inline limits (executor.ts から逐語移設) */
export const ARTIFACT_INLINE_LIMIT = 1024 * 1024; // 1 MiB
export const BINARY_PROBE_SIZE = 8192; // 8 KB — probe window for null-byte binary detection

/**
 * MAESTRO ジョブの terminal ステータスセット。
 * executor.ts と reconciler.ts の両方で共有し、定義の漂流を防ぐ。
 */
export const TERMINAL_JOB_STATUSES = new Set([
  'succeeded',
  'failed',
  'cancelled',
  'waiting_human',
]);

/**
 * workspacePath/output/ 直下の通常ファイルを列挙して Artifact 形式で返す。
 *
 * - workspacePath が null、または output/ が存在しない → []
 * - 1 MiB 超過 → omitted メッセージ
 * - 先頭 8KB に null byte → not inlined メッセージ
 * - それ以外 → utf-8 インライン
 */
export function enumerateOutputArtifacts(
  workspacePath: string | null,
): Array<{ artifactId: string; name: string; text: string }> {
  if (!workspacePath) return [];

  const outputDir = path.join(workspacePath, 'output');
  if (!fs.existsSync(outputDir)) return [];

  let entries: fs.Dirent[] = [];
  try {
    entries = fs.readdirSync(outputDir, { withFileTypes: true });
  } catch (err) {
    logger.warn(`[a2a-task-finalize] output dir read failed: ${err}`);
    return [];
  }

  const results: Array<{ artifactId: string; name: string; text: string }> = [];
  for (const entry of entries) {
    if (!entry.isFile()) continue;
    try {
      const filePath = path.join(outputDir, entry.name);
      const buf = fs.readFileSync(filePath);
      let text: string;
      if (buf.length > ARTIFACT_INLINE_LIMIT) {
        text = `[file ${entry.name} omitted: ${buf.length} bytes exceeds inline limit]`;
      } else {
        const probe = buf.slice(0, Math.min(BINARY_PROBE_SIZE, buf.length));
        if (probe.indexOf(0) !== -1) {
          text = `[binary file ${entry.name} (${buf.length} bytes) not inlined]`;
        } else {
          text = buf.toString('utf-8');
        }
      }
      results.push({ artifactId: crypto.randomUUID(), name: entry.name, text });
    } catch (err) {
      logger.warn(`[a2a-task-finalize] artifact read failed: ${err}`);
    }
  }
  return results;
}

/**
 * MAESTRO ジョブの terminal status を A2A 終端状態に写像する。
 *
 * - succeeded       → { state: 'completed' }
 * - cancelled       → { state: 'canceled' }
 * - waiting_human   → { state: 'input-required', message: '...' }
 * - failed / その他 → { state: 'failed', message: errorSummary ?? abortReason ?? fallback }
 */
export function finalizeStatusFromJob(job: {
  status: string;
  errorSummary?: string | null;
  abortReason?: string | null;
}): {
  state: 'completed' | 'failed' | 'canceled' | 'input-required';
  message?: string;
} {
  switch (job.status) {
    case 'succeeded':
      return { state: 'completed' };

    case 'cancelled':
      return { state: 'canceled' };

    case 'waiting_human':
      return { state: 'input-required', message: 'interactive input not supported in this version' };

    default:
      // 'failed' および未知の terminal ステータス
      return {
        state: 'failed',
        message: job.errorSummary ?? job.abortReason ?? 'job failed',
      };
  }
}
