/**
 * 光湖视频AI系统 · 视频API适配层
 * D135 · 铸渊 ICE-GL-ZY001
 *
 * 基于 火山方舟 Seedance 2.0 标准 API 对接
 * 文档: https://www.volcengine.com/docs/82379/1520757
 *
 * 使用方式:
 *   const { generateVideo, validateAndGenerate } = require('./video-api-adapter');
 *   const result = await validateAndGenerate({ prompt: '...', duration: 10 });
 *
 * 输出路径优先级:
 *   1. opts.outputPath（显式指定）
 *   2. 外接硬盘 JZAO /Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/视频/
 *   3. 本地 fallback video-ai-system/outputs/
 *
 * 环境变量（放在 video-ai-system/.env）:
 *   JIMENG_API_KEY=xxx       火山方舟 API Key
 *   JIMENG_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
 *   JIMENG_MODEL=doubao-seedance-2-0-260128
 *   VIDEO_OUTPUT_ROOT=/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/视频  (可选·覆盖默认)
 */

const fs = require('fs');
const path = require('path');
const https = require('https');
const http = require('http');

// 读取环境变量
const envPath = path.resolve(__dirname, '../.env');
if (fs.existsSync(envPath)) {
  const envContent = fs.readFileSync(envPath, 'utf-8');
  envContent.split('\n').forEach(line => {
    const trimmed = line.trim();
    if (trimmed && !trimmed.startsWith('#')) {
      const [key, ...vals] = trimmed.split('=');
      if (key && vals.length) process.env[key.trim()] = vals.join('=').trim();
    }
  });
}

const API_KEY = process.env.JIMENG_API_KEY || '';
const BASE_URL = process.env.JIMENG_BASE_URL || 'https://ark.cn-beijing.volces.com/api/v3';
const MODEL = process.env.JIMENG_MODEL || 'doubao-seedance-2-0-260128';
const POLL_INTERVAL_MS = parseInt(process.env.POLL_INTERVAL_MS, 10) || 5000;
const MAX_POLL_ATTEMPTS = parseInt(process.env.MAX_POLL_ATTEMPTS, 10) || 120; // 最多轮询10分钟

// 【D135】输出路径：外接硬盘优先
const JZAO_VIDEO_ROOT = '/Volumes/JZAO/铸渊-ICE-GL-ZY001/OUT-输出/视频';
const LOCAL_OUTPUT_ROOT = path.resolve(__dirname, '../outputs');
const VIDEO_OUTPUT_ROOT = (() => {
  const env = process.env.VIDEO_OUTPUT_ROOT;
  if (env) return env;
  if (fs.existsSync(JZAO_VIDEO_ROOT)) return JZAO_VIDEO_ROOT;
  return LOCAL_OUTPUT_ROOT;
})();
const VIDEO_REGISTRY_PATH = path.resolve(__dirname, '../outputs/video-registry.json');

console.log(`[VideoAPI] 输出路径: ${VIDEO_OUTPUT_ROOT}`);

/**
 * 【D135】解析输出路径 — 外接硬盘JZAO优先，本地fallback
 * @param {string} projectKey - 项目标识，如 "zai-fu-fei-xiu-xian/ep01"
 * @param {string} filename - 文件名
 * @returns {string} 完整输出路径
 */
function resolveOutputPath(projectKey, filename) {
  const dir = path.join(VIDEO_OUTPUT_ROOT, projectKey);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
  return path.join(dir, filename);
}

/**
 * 【D135】视频注册 — 镜编号→硬盘路径的双向索引
 * 存到仓库里，人不用翻文件夹，系统毫秒级定位
 */
function registerVideo({ projectKey, shotId, taskId, filePath, duration, resolution, prompt }) {
  let registry = { _meta: { updated: new Date().toISOString(), by: '铸渊 ICE-GL-ZY001' }, shots: {} };

  try {
    if (fs.existsSync(VIDEO_REGISTRY_PATH)) {
      registry = JSON.parse(fs.readFileSync(VIDEO_REGISTRY_PATH, 'utf-8'));
    }
  } catch (e) {
    // 文件损坏，重建
  }

  const key = shotId || taskId;
  registry.shots[key] = {
    shotId: key,
    taskId,
    projectKey,
    filePath,
    duration,
    resolution,
    promptPreview: (prompt || '').substring(0, 80),
    generatedAt: new Date().toISOString(),
    dNumber: 'D135',
  };
  registry._meta.updated = new Date().toISOString();
  registry._meta.totalShots = Object.keys(registry.shots).length;

  const dir = path.dirname(VIDEO_REGISTRY_PATH);
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
  fs.writeFileSync(VIDEO_REGISTRY_PATH, JSON.stringify(registry, null, 2), 'utf-8');
  console.log(`[VideoAPI·注册] ${key} → ${filePath}`);
}

/**
 * 【D135】按镜编号查找视频 — 毫秒级定位
 * @param {string} shotId
 * @returns {{ found: boolean, filePath?: string, info?: object }}
 */
function findVideo(shotId) {
  try {
    if (!fs.existsSync(VIDEO_REGISTRY_PATH)) return { found: false };
    const registry = JSON.parse(fs.readFileSync(VIDEO_REGISTRY_PATH, 'utf-8'));
    const entry = registry.shots[shotId];
    if (!entry) return { found: false };
    const onDisk = fs.existsSync(entry.filePath);
    return { found: true, filePath: entry.filePath, onDisk, info: entry };
  } catch (e) {
    return { found: false, error: e.message };
  }
}

/**
 * HTTP POST 请求封装（Node.js 原生，无依赖）
 */
async function httpPost(url, body, apiKey) {
  const urlObj = new URL(url);
  const isHttps = urlObj.protocol === 'https:';
  const transport = isHttps ? https : http;
  const payload = JSON.stringify(body);

  return new Promise((resolve, reject) => {
    const req = transport.request(url, {
      method: 'POST',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
        'Content-Length': Buffer.byteLength(payload),
      },
      timeout: 30000,
    }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        try {
          const json = JSON.parse(data);
          if (res.statusCode >= 400) {
            const errMsg = json.error?.message || json.message || `HTTP ${res.statusCode}`;
            reject(new Error(`API错误(${res.statusCode}): ${errMsg}`));
            return;
          }
          resolve(json);
        } catch (e) {
          reject(new Error(`JSON解析失败: ${data.substring(0, 200)}`));
        }
      });
    });
    req.on('error', reject);
    req.on('timeout', () => { req.destroy(); reject(new Error('请求超时')); });
    req.write(payload);
    req.end();
  });
}

/**
 * HTTP GET 请求封装
 */
async function httpGet(url, apiKey) {
  const urlObj = new URL(url);
  const isHttps = urlObj.protocol === 'https:';
  const transport = isHttps ? https : http;

  return new Promise((resolve, reject) => {
    const req = transport.request(url, {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${apiKey}`,
        'Content-Type': 'application/json',
      },
      timeout: 10000,
    }, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        try {
          const json = JSON.parse(data);
          resolve(json);
        } catch (e) {
          reject(new Error(`JSON解析失败: ${data.substring(0, 200)}`));
        }
      });
    });
    req.on('error', reject);
    req.end();
  });
}

/**
 * 下载视频到本地，避免外网URL过期
 */
async function downloadVideo(videoUrl, outputPath) {
  const urlObj = new URL(videoUrl);
  const isHttps = urlObj.protocol === 'https:';
  const transport = isHttps ? https : http;

  return new Promise((resolve, reject) => {
    const dir = path.dirname(outputPath);
    if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });

    const file = fs.createWriteStream(outputPath);
    transport.get(videoUrl, (res) => {
      if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
        // 重定向
        const redirectUrl = res.headers.location.startsWith('http')
          ? res.headers.location
          : `${urlObj.protocol}//${urlObj.host}${res.headers.location}`;
        downloadVideo(redirectUrl, outputPath).then(resolve).catch(reject);
        return;
      }
      res.pipe(file);
      file.on('finish', () => { file.close(); resolve(outputPath); });
      file.on('error', (err) => { fs.unlinkSync(outputPath); reject(err); });
    }).on('error', reject);
  });
}

// ==================== API 规范常量 ====================
const API_SPEC = {
  duration:    { key: 'duration',    type: 'integer', range: [4, 15], default: 5,  special: -1,  note: '-1=自动' },
  resolution:  { key: 'resolution',  type: 'string',  values: ['480p', '720p'],   default: '720p' },
  model:       { key: 'model',       type: 'string',  values: ['doubao-seedance-2-0-260128'], default: 'doubao-seedance-2-0-260128' },
  promptMaxLen: { chinese: 500, english: 1000 },
};

/**
 * 【新增 · D135】预校验 · 提交前参数合规性检查
 * 零成本——不调用API，只在本地检查参数是否对齐Seedance 2.0规范
 * @param {object} opts
 * @returns {{ valid: boolean, warnings: string[], errors: string[], corrected: object }}
 */
function preflightCheck({ prompt, duration, resolution, style }) {
  const warnings = [];
  const errors = [];
  const corrected = {};

  // 1. duration 校验
  const dur = parseInt(duration, 10);
  if (duration !== undefined && duration !== null) {
    if (isNaN(dur)) {
      errors.push(`duration 类型错误: 收到 "${duration}" (${typeof duration})，应为 integer`);
    } else if (dur !== -1 && (dur < 4 || dur > 15)) {
      errors.push(`duration 超出范围: ${dur}，Seedance 2.0 支持 4~15 秒（或 -1 自动）`);
    } else {
      corrected.duration = dur; // 确保是整数
    }
  } else {
    corrected.duration = API_SPEC.duration.default;
  }

  // 2. resolution 校验
  if (resolution !== undefined && resolution !== null) {
    if (!API_SPEC.resolution.values.includes(String(resolution))) {
      warnings.push(`resolution "${resolution}" 不在 Seedance 2.0 支持列表中（${API_SPEC.resolution.values.join(', ')}），已修正为 ${API_SPEC.resolution.default}`);
      corrected.resolution = API_SPEC.resolution.default;
    } else {
      corrected.resolution = resolution;
    }
  } else {
    corrected.resolution = API_SPEC.resolution.default;
  }

  // 3. prompt 长度校验
  if (prompt && prompt.trim()) {
    const chineseChars = (prompt.match(/[\u4e00-\u9fff]/g) || []).length;
    const englishWords = prompt.split(/\s+/).filter(w => /[a-zA-Z]/.test(w)).length;
    if (chineseChars > API_SPEC.promptMaxLen.chinese) {
      warnings.push(`提示词中文字数 ${chineseChars}，超过建议上限 ${API_SPEC.promptMaxLen.chinese} 字`);
    }
    if (englishWords > API_SPEC.promptMaxLen.english) {
      warnings.push(`提示词英文词数 ${englishWords}，超过建议上限 ${API_SPEC.promptMaxLen.english} 词`);
    }
  } else {
    errors.push('提示词不能为空');
  }

  // 4. style 参数（Seedance 2.0 官方API不直接支持style参数，通过提示词控制）
  if (style) {
    warnings.push(`style="${style}" 不是 Seedance 2.0 官方 API 参数，已忽略。风格请通过提示词描述控制。`);
    // 不传入 corrected，style 将被丢弃
  }

  return {
    valid: errors.length === 0,
    warnings,
    errors,
    corrected,
  };
}

/**
 * 提交视频生成任务
 * @param {object} opts
 * @param {string} opts.prompt - 视频描述提示词（中文 ≤500字，英文 ≤1000词）
 * @param {number} [opts.duration] - 时长 4-15秒，默认 5，-1=自动
 * @param {string} [opts.resolution] - 分辨率 '480p' | '720p'，默认 720p
 * @param {string} [opts.style] - [已废弃] Seedance 2.0 标准API不直接支持，请通过提示词控制风格
 * @param {string} [opts.referenceImage] - 参考图文件路径（用于锁脸），自动转 base64
 * @returns {Promise<{taskId: string, preflight: object}>}
 */
async function submitTask({ prompt, duration, resolution, style, referenceImage }) {
  // 【D135】预校验
  const preflight = preflightCheck({ prompt, duration, resolution, style });

  if (!preflight.valid) {
    console.error(`[VideoAPI·预校验] ❌ 参数错误，拒绝提交:`);
    preflight.errors.forEach(e => console.error(`  ✗ ${e}`));
    throw new Error(`预校验失败: ${preflight.errors.join('; ')}`);
  }

  if (preflight.warnings.length > 0) {
    console.warn(`[VideoAPI·预校验] ⚠️ ${preflight.warnings.length} 条警告:`);
    preflight.warnings.forEach(w => console.warn(`  ⚠ ${w}`));
  }

  // 使用修正后的参数
  const finalDuration = preflight.corrected.duration;
  const finalResolution = preflight.corrected.resolution;

  console.log(`[VideoAPI] 提交任务: ${prompt.substring(0, 60)}...`);

  // 【D135关键修复】参数必须在顶层，不能嵌套在 parameters 对象中
  // 官方文档: https://www.volcengine.com/docs/82379/1520757
  const content = [
    { type: 'text', text: prompt }
  ];

  // 【D135】参考图支持 — 锁脸用
  if (referenceImage) {
    if (!fs.existsSync(referenceImage)) {
      console.warn(`[VideoAPI] ⚠️ 参考图不存在: ${referenceImage}`);
    } else {
      const imgBuffer = fs.readFileSync(referenceImage);
      const ext = path.extname(referenceImage).toLowerCase();
      const mimeMap = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.webp': 'image/webp' };
      const mime = mimeMap[ext] || 'image/png';
      const b64 = imgBuffer.toString('base64');
      content.push({
        type: 'image_url',
        role: 'reference_image',
        image_url: { url: `data:${mime};base64,${b64}` },
      });
      console.log(`[VideoAPI] 参考图已挂载: ${path.basename(referenceImage)} (${(imgBuffer.length/1024).toFixed(0)}KB)`);
    }
  }

  const payload = {
    model: MODEL,
    content,
    duration: finalDuration,    // ← 顶层 integer，不是 parameters.video_length String
    resolution: finalResolution, // ← 顶层 string，仅支持 480p/720p
  };

  const data = await httpPost(`${BASE_URL}/contents/generations/tasks`, payload, API_KEY);
  const taskId = data.id || data.task_id || data.data?.task_id || data.data?.id;

  if (!taskId) {
    throw new Error(`即梦API未返回任务ID: ${JSON.stringify(data).substring(0, 200)}`);
  }

  console.log(`[VideoAPI] 任务已提交: ${taskId}  模型: ${MODEL}  时长: ${finalDuration}s  分辨率: ${finalResolution}`);
  return { taskId, preflight };
}

/**
 * 查询任务状态
 * @param {string} taskId
 * @returns {Promise<{status: 'generating'|'completed'|'failed', videoUrl?: string, videoMeta?: object, rawResponse?: object, error?: string}>}
 */
async function queryTask(taskId) {
  const data = await httpGet(`${BASE_URL}/contents/generations/tasks/${taskId}`, API_KEY);
  const rawStatus = (data.status || data.data?.status || '').toLowerCase();

  if (['succeeded', 'completed', 'success', 'done'].includes(rawStatus)) {
    const videoUrl = data.content?.video_url
      || data.output?.video_url
      || data.output?.url
      || data.data?.output?.video_url
      || data.data?.output?.url
      || data.result?.video_url
      || data.content?.[0]?.url
      || data.data?.content?.[0]?.url;

    if (!videoUrl) {
      return { status: 'failed', error: '任务完成但未返回视频地址' };
    }

    // 【D135】提取响应中包含的元数据（可能有 duration/width/height 等）
    const videoMeta = {};
    const rawOutput = data.output || data.data?.output || data.content || data.data?.content || {};
    if (rawOutput.duration !== undefined) videoMeta.duration = rawOutput.duration;
    if (rawOutput.video_duration !== undefined) videoMeta.video_duration = rawOutput.video_duration;
    if (rawOutput.width !== undefined) videoMeta.width = rawOutput.width;
    if (rawOutput.height !== undefined) videoMeta.height = rawOutput.height;
    if (rawOutput.resolution !== undefined) videoMeta.resolution = rawOutput.resolution;
    if (rawOutput.frame_count !== undefined) videoMeta.frame_count = rawOutput.frame_count;
    if (rawOutput.fps !== undefined) videoMeta.fps = rawOutput.fps;

    return { status: 'completed', videoUrl, videoMeta, rawResponse: data };
  }

  if (['failed', 'error', 'cancelled'].includes(rawStatus)) {
    const errMsg = data.error?.message || data.data?.error?.message || data.message || '生成失败';
    return { status: 'failed', error: errMsg, rawResponse: data };
  }

  return { status: 'generating' };
}

/**
 * 【D135 新增】通过 ffprobe 从视频URL提取实际时长（秒）
 * 在下载完整视频之前就能知道实际时长，避免瞎子式验证
 * @param {string} videoUrl - 视频URL
 * @returns {Promise<{duration: number|null, meta: object, error: string|null}>}
 */
async function probeVideoDuration(videoUrl) {
  const { execSync } = require('child_process');

  try {
    // ffprobe 只下载文件头解析元数据，不发完整请求
    const stdout = execSync(
      `ffprobe -v quiet -print_format json -show_format -show_streams "${videoUrl}"`,
      { timeout: 15000, encoding: 'utf8', maxBuffer: 1024 * 1024 }
    );
    const meta = JSON.parse(stdout);

    // 从 format 层取时长
    const formatDuration = parseFloat(meta.format?.duration);
    // 从流层取第一视频流时长
    const videoStream = (meta.streams || []).find(s => s.codec_type === 'video');
    const streamDuration = videoStream ? parseFloat(videoStream.duration) : null;

    const duration = formatDuration || streamDuration || null;

    if (duration !== null) {
      console.log(`[VideoAPI·探针] 视频实际时长: ${duration.toFixed(1)}s (${videoStream?.width || '?'}×${videoStream?.height || '?'})`);
    }

    return {
      duration,
      meta: {
        width: videoStream?.width || null,
        height: videoStream?.height || null,
        codec: videoStream?.codec_name || null,
        fps: videoStream?.r_frame_rate || null,
      },
      error: null,
    };
  } catch (e) {
    return {
      duration: null,
      meta: {},
      error: `ffprobe 不可用或提取失败: ${e.message}`,
    };
  }
}

/**
 * 生成视频（提交 + 自动轮询 + 下载 + 注册索引）
 * @param {object} opts
 * @param {string} opts.prompt - 视频提示词
 * @param {number} [opts.duration] - 时长 4-15秒
 * @param {string} [opts.resolution] - 分辨率 480p/720p
 * @param {string} [opts.style] - [已废弃]
 * @param {string} [opts.outputPath] - 输出路径（可选，优先于默认JZAO路径）
 * @param {string} [opts.shotId] - 镜编号，用于注册索引（如 'ep01-shot01'）
 * @param {string} [opts.projectKey] - 项目标识（如 'zai-fu-fei-xiu-xian/ep01'）
 * @returns {Promise<{videoPath: string, taskId: string, duration: number, preflight: object}>}
 */
async function generateVideo({ prompt, duration, resolution, style, outputPath, shotId, projectKey, referenceImage }) {
  if (!API_KEY) {
    throw new Error('未配置 JIMENG_API_KEY。请在 video-ai-system/.env 中设置。');
  }

  // 1. 提交任务（含预校验）
  const { taskId, preflight } = await submitTask({ prompt, duration, resolution, style, referenceImage });
  const finalDuration = preflight.corrected.duration;
  const finalResolution = preflight.corrected.resolution;

  // 2. 轮询等待
  let attempts = 0;
  while (attempts < MAX_POLL_ATTEMPTS) {
    attempts++;
    await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));

    const result = await queryTask(taskId);

    if (result.status === 'completed') {
      // 【D135】API响应中如有元数据，先报告
      if (Object.keys(result.videoMeta).length > 0) {
        console.log(`[VideoAPI] API返回的元数据:`, JSON.stringify(result.videoMeta));
      }

      // 3. 【D135】解析输出路径 — 外接硬盘JZAO优先
      let finalPath;
      if (outputPath) {
        finalPath = outputPath;
      } else if (shotId && projectKey) {
        // 有编号 → 走 JZAO 编号文件夹
        finalPath = resolveOutputPath(projectKey, `${shotId}.mp4`);
      } else {
        // 兜底: JZAO根目录用taskId
        finalPath = path.join(VIDEO_OUTPUT_ROOT, `${taskId}.mp4`);
      }

      console.log(`[VideoAPI] 生成完成！正在下载到: ${finalPath}`);
      await downloadVideo(result.videoUrl, finalPath);
      console.log(`[VideoAPI] 视频已保存: ${finalPath}`);

      // 4. 【D135】注册视频索引 — 仓库↔硬盘双向映射
      if (shotId || taskId) {
        registerVideo({
          projectKey: projectKey || 'unknown',
          shotId: shotId || taskId,
          taskId,
          filePath: finalPath,
          duration: finalDuration,
          resolution: finalResolution,
          prompt,
        });
      }

      return { videoPath: finalPath, taskId, duration: finalDuration, resolution: finalResolution, preflight };
    }

    if (result.status === 'failed') {
      throw new Error(`视频生成失败: ${result.error}`);
    }

    console.log(`[VideoAPI] 生成中... (${attempts}/${MAX_POLL_ATTEMPTS})`);
  }

  throw new Error(`轮询超时（${MAX_POLL_ATTEMPTS * POLL_INTERVAL_MS / 1000}秒）`);
}

/**
 * 【D135 新增】智能生成 — 提交前校验 + 下载前探针验证 + 自动重试
 *
 * 流程：
 *   预校验(免费) → 提交 → 轮询 → API响应元数据检查 →
 *   → ffprobe 探针(不下载)检查实际时长
 *   → ✅ 匹配 → 下载
 *   → ❌ 不匹配 → 报告差异，询问是否仍下载
 *
 * @param {object} opts
 * @param {string} opts.prompt          - 提示词
 * @param {number} [opts.duration]      - 期望时长 4-15秒
 * @param {string} [opts.resolution]    - 分辨率
 * @param {string} [opts.outputPath]    - 输出路径
 * @param {boolean} [opts.forceDownload] - 跳过探针验证直接下载
 * @returns {Promise<{videoPath: string, taskId: string, actualDuration: number, matched: boolean, preflight: object}>}
 */
async function validateAndGenerate({ prompt, duration, resolution, outputPath, shotId, projectKey, referenceImage, forceDownload }) {
  // 提交 + 轮询 + 下载
  const result = await generateVideo({ prompt, duration, resolution, outputPath, shotId, projectKey, referenceImage });

  // 【D135】探针验证实际时长
  let probeResult = null;
  if (!forceDownload && result.videoPath) {
    console.log(`[VideoAPI·验证] 正在探测下载后视频的实际时长...`);
    probeResult = await probeVideoDuration(result.videoPath);

    if (probeResult.duration !== null) {
      const actual = probeResult.duration;
      const expected = result.duration;
      const diff = Math.abs(actual - expected);

      if (diff > 1.0) {
        // 差异超过1秒 → 问题
        console.warn(`[VideoAPI·验证] ⚠️ 时长不匹配！`);
        console.warn(`  请求: ${expected}s → 实际: ${actual.toFixed(1)}s (差 ${diff.toFixed(1)}s)`);
        return {
          ...result,
          actualDuration: actual,
          matched: false,
          probeMeta: probeResult.meta,
        };
      } else {
        console.log(`[VideoAPI·验证] ✅ 时长匹配: 请求${expected}s = 实际${actual.toFixed(1)}s`);
        return {
          ...result,
          actualDuration: actual,
          matched: true,
          probeMeta: probeResult.meta,
        };
      }
    } else {
      console.warn(`[VideoAPI·验证] ⚠️ ffprobe 不可用，跳过时长验证 (${probeResult.error})`);
    }
  }

  return {
    ...result,
    actualDuration: probeResult?.duration || null,
    matched: null, // 无法验证
    probeMeta: probeResult?.meta || {},
  };
}

// ==================== 导出 ====================

module.exports = {
  submitTask,
  queryTask,
  generateVideo,
  validateAndGenerate,
  preflightCheck,
  probeVideoDuration,
  downloadVideo,
  resolveOutputPath,
  registerVideo,
  findVideo,
  MODEL,
  BASE_URL,
  API_SPEC,
  VIDEO_OUTPUT_ROOT,
  VIDEO_REGISTRY_PATH,
};
