import * as path from 'path';
import * as fs from 'fs';
import ora from 'ora';
import { success, info, warning } from '../utils/logger.js';
import { outputSuccess, outputError, isPrettyMode } from '../utils/output.js';
import { addEnhancedHelp } from '../utils/helpText.js';
import { GenerationApiClient, GenerationApiError } from '../services/generation-api.js';
import { registerDoctorSubcommands } from './doctor.js';
/**
 * `gen avatar` — 数字人分身完整 namespace.
 *
 * 低层 (本文件):
 *   gen avatar train <video-file> --external-id <id>   训练 avatar look
 *
 * 高层 (来自 doctor.ts,作为 alias 也注册在 gen doctor 下):
 *   gen avatar onboard <audio> <video>                 一次性训 voice + avatar
 *   gen avatar video --avatar <id> --voice <id>        用 trained avatar 合成视频
 *   gen avatar profile <external-id>                   查 profile (voice_id / look_id / 状态)
 *
 * gen doctor * 是历史命名 (Phase 2 POC 起源于医生场景),保留作为兼容性别名,
 * 行为完全一致。新用户 / 文档推荐用 gen avatar *。
 *
 * Phase 2 doctor-avatar feature. See spec §13 + §13.4 (clean-bg requirement).
 */
export function registerAvatarCommand(program) {
    const avatar = program.command('avatar').description('数字人分身相关命令 (train / onboard / video / profile)');
    // 高层 facade (与 gen doctor * 等价)
    registerDoctorSubcommands(avatar);
    // 低层 train (仅在 gen avatar 下注册,不在 gen doctor 下出现)
    const trainCmd = avatar
        .command('train')
        .description('用本地视频训练 HeyGen digital_twin avatar,产出 avatar look_id')
        .argument('<video-file>', '视频文件路径(.mp4 / .mov 等,2-5 分钟推荐)')
        .requiredOption('--external-id <id>', '业务方医生标识(写入 doctor_avatar_profiles)')
        .option('--name <name>', 'HeyGen 侧 avatar name(默认 doctor-avatar)')
        .option('--clean-bg', '声明视频背景已干净(白墙/医院/棚拍/绿幕)', false)
        .option('--accept-baked-bg', '明确接受脏背景被 baked 进 avatar look(spec §13.4 — 仅用于探针 / 非上线场景)', false)
        .action(async (videoFile, options) => {
        const pretty = isPrettyMode(options);
        try {
            // §13.4: video background gets baked into avatar look. Force the operator to make
            // an explicit choice — either declare clean-bg or accept the consequences.
            if (!options.cleanBg && !options.acceptBakedBg) {
                outputError('BG_DECISION_REQUIRED', '请二选一(spec §13.4 — 背景会被 baked 进 avatar look,无法撤销):\n' +
                    '  --clean-bg          视频背景已干净(白墙/医院/棚拍/绿幕)\n' +
                    '  --accept-baked-bg   接受当前视频背景成为 avatar 永久背景(仅探针/非上线场景)', options);
                return;
            }
            if (options.cleanBg && options.acceptBakedBg) {
                outputError('BG_DECISION_CONFLICT', '--clean-bg 和 --accept-baked-bg 二选一,不能同时指定', options);
                return;
            }
            const videoPath = path.resolve(videoFile);
            if (!fs.existsSync(videoPath)) {
                outputError('VIDEO_NOT_FOUND', `视频文件不存在: ${videoPath}`, options);
                return;
            }
            if (pretty && options.acceptBakedBg) {
                warning('已显式接受脏背景被 baked,生成视频时无法替换 avatar 内置背景');
            }
            const client = new GenerationApiClient();
            // Step 1: upload video asset
            const upSpinner = pretty ? ora('上传视频到 HeyGen 资产存储...').start() : null;
            const asset = await client.uploadHeyGenAsset(videoPath);
            if (pretty)
                upSpinner.succeed(`视频已上传 (asset_id=${asset.asset_id}, ${(asset.size_bytes / 1024 / 1024).toFixed(1)} MB)`);
            // Step 2: submit avatar-train task
            const trainSpinner = pretty ? ora('提交 avatar 训练任务...').start() : null;
            const result = await client.submitAndPoll('avatar-train', {
                video_asset_id: asset.asset_id,
                external_id: options.externalId,
                name: options.name,
                training_video_clean_bg: options.cleanBg,
            }, {
                timeout: 1800000, // 30 min — avatar training can be slow
                onProgress: (status) => {
                    if (pretty)
                        trainSpinner.text = `avatar 训练中 (${status})...`;
                },
            });
            if (result.status === 'completed') {
                const groupId = result.result_meta?.avatar_group_id;
                const lookIds = result.result_meta?.look_ids ?? [];
                const looksCount = result.result_meta?.looks_count;
                const previewUrl = result.result_meta?.preview_image_url;
                if (pretty) {
                    trainSpinner.succeed('avatar 训练完成!');
                    success(`avatar_group_id: ${groupId}`);
                    success(`avatar look_id (用于 video gen): ${lookIds[0] || '(none captured)'}`);
                    info(`looks_count: ${looksCount} (Phase 2 v1 仅存第一个 look,见 spec §13.5)`);
                    if (previewUrl)
                        info(`预览图: ${previewUrl}`);
                    info(`task_id: ${result.task_id}`);
                    info(`external_id: ${options.externalId}`);
                    info(`使用: gen doctor profile ${options.externalId} 查看完整 profile`);
                }
                else {
                    outputSuccess({
                        task_id: result.task_id,
                        status: result.status,
                        avatar_group_id: groupId,
                        look_ids: lookIds,
                        looks_count: looksCount,
                        preview_image_url: previewUrl,
                        external_id: options.externalId,
                    });
                }
            }
            else {
                // task status != completed → 失败。让顶层 success:false 并 exit 1，否则
                // Agent 会把 failure 当 success（参见 image.ts 同样修复）。
                const errCode = result.error_code ?? 'GENERATION_FAILED';
                const errMsg = result.error_message ?? `任务状态: ${result.status}`;
                if (pretty) {
                    trainSpinner.fail(errMsg);
                    if (result.error_code)
                        info(`错误码: ${result.error_code}${result.retryable ? '（可重试）' : ''}`);
                    info(`使用 'gen task get ${result.task_id}' 查看最新状态`);
                    process.exit(1);
                }
                else {
                    outputError(errCode, errMsg, options, {
                        task_id: result.task_id,
                        status: result.status,
                        retryable: result.retryable ?? false,
                        error_class: result.error_class ?? null,
                    });
                }
            }
        }
        catch (err) {
            if (err instanceof GenerationApiError) {
                outputError(err.code, err.message, options);
            }
            else {
                outputError('AVATAR_TRAIN_FAILED', `avatar 训练失败: ${err}`, options);
            }
        }
    });
    addEnhancedHelp(trainCmd, {
        examples: [
            '# 训练医生 avatar (棚拍/白墙背景)',
            '$ gen avatar train ./doctor-video.mp4 --external-id doc-123 --clean-bg',
            '',
            '# 视频背景不干净(将来需抠图)— 先训出来再讨论',
            '$ gen avatar train ./hospital-recording.mp4 --external-id doc-123',
        ],
        outputJson: `{
  "success": true,
  "data": {
    "task_id": "task-uuid",
    "status": "completed",
    "avatar_group_id": "174fa88e...",
    "look_ids": ["04182555..."],
    "looks_count": 4,
    "preview_image_url": "https://heygen/preview.jpg",
    "external_id": "doc-123"
  }
}`,
        relatedCommands: [
            { command: 'voice clone <audio-file> --external-id <id>', description: '训练 voice' },
            { command: 'doctor onboard <audio> <video> --external-id <id>', description: '一次同时训 voice + avatar' },
            { command: 'doctor profile <external-id>', description: '查看医生 profile' },
        ],
        notes: [
            '推荐视频 2-5 分钟,正面拍摄,光线均匀',
            '**spec §13.4**: HeyGen 把整段视频画面(包括背景)整体作为 avatar look — 上线产品要求干净背景',
            'looks_count 通常 = 4,但 Phase 2 v1 只存第一个 look_id(待 v1.1 扩展)',
            '训练时长 5-30 分钟',
        ],
    });
}
//# sourceMappingURL=avatar.js.map