/**
 * ═══════════════════════════════════════════════════════════
 * 模块H · 开源模型微调引擎 MCP 工具
 * ═══════════════════════════════════════════════════════════
 *
 * 签发: 铸渊 · ICE-GL-ZY001
 * 版权: 国作登字-2026-A-00037559
 *
 * 冰朔D62核心指令: 接入开源模型，用COS桶训练数据直接做微调
 * 本质: 同一份TCS结构化数据，两种用途 — RAG + 微调
 *
 * 架构理念:
 *   现有RAG训练 → 用API调用商业模型，人格体"脑子"在COS桶里
 *   开源模型微调 → 用同一份数据，把"脑子"直接装进开源模型
 *   二者并行运行 → 微调模型优先 → 不可用时降级回API模型
 *
 * 工具清单:
 *   finetuneExportDataset   — 导出TCS语料为微调JSONL格式
 *   finetuneSubmitJob       — 提交微调任务到DeepSeek/Qwen
 *   finetuneCheckStatus     — 查询微调任务进度
 *   finetuneRegisterModel   — 注册微调完成的模型
 *   finetuneListModels      — 列出已注册的微调模型
 *   finetuneCallModel       — 调用微调模型进行推理
 *   finetuneCompareModels   — A/B测试微调 vs 基座模型
 *   finetuneGetCostEstimate — 估算微调成本
 */

'use strict';

const https = require('https');
const crypto = require('crypto');
const cos = require('../cos');

// ─── 微调 API 配置 ───
const FINETUNE_PROVIDERS = {
  deepseek: {
    host: 'api.deepseek.com',
    createPath: '/fine_tuning/jobs',
    statusPath: '/fine_tuning/jobs/',
    uploadPath: '/files',
    inferencePath: '/v1/chat/completions',
    defaultModel: 'deepseek-chat',
    keyEnv: 'ZY_DEEPSEEK_API_KEY',
    label: 'DeepSeek微调'
  },
  qwen: {
    host: 'dashscope.aliyuncs.com',
    createPath: '/api/v1/fine-tunes',
    statusPath: '/api/v1/fine-tunes/',
    uploadPath: '/api/v1/files',
    inferencePath: '/compatible-mode/v1/chat/completions',
    defaultModel: 'qwen-max',
    keyEnv: 'ZY_QIANWEN_API_KEY',
    label: 'Qwen/DashScope微调'
  }
};

// ─── 推理降级配置（与training-agent-ops.js同源） ───
const LLM_CONFIGS = {
  'deepseek-chat': {
    host: 'api.deepseek.com',
    path: '/v1/chat/completions',
    model: 'deepseek-chat',
    keyEnv: 'ZY_DEEPSEEK_API_KEY',
    purpose: '微调基座·推理降级'
  },
  'qwen-max': {
    host: 'dashscope.aliyuncs.com',
    path: '/compatible-mode/v1/chat/completions',
    model: 'qwen-max',
    keyEnv: 'ZY_QIANWEN_API_KEY',
    purpose: '微调基座·推理降级'
  }
};

// ─── 成本估算参数（2026-04 参考价，实际以provider当月公告为准） ───
const COST_PER_1K_TOKENS = {
  deepseek: 0.014,  // 约 ¥0.014 / 1K tokens（训练）· 2026-04 参考
  qwen: 0.020       // 约 ¥0.020 / 1K tokens（训练）· 2026-04 参考
};

// ─── 常量 ───
const DEFAULT_BUCKET = 'cold';
const MAX_SAMPLES_DEFAULT = 500;
const FINETUNE_TIMEOUT = 60000;

// ═══════════════════════════════════════════════════════════
// 工具实现
// ═══════════════════════════════════════════════════════════

/**
 * finetuneExportDataset — 导出TCS语料为微调JSONL格式
 *
 * 将COS桶中的TCS结构化语料转换为 instruction/input/output 三元组
 * JSONL格式，直接用于提交到微调API
 *
 * input:
 *   persona_id: string      — 人格体ID
 *   corpus_bucket: string   — 语料桶（默认cold）
 *   corpus_prefix: string   — 语料路径前缀
 *   output_format: string   — 输出格式（默认jsonl）
 *   max_samples: number     — 最大样本数
 */
async function finetuneExportDataset(input) {
  const { persona_id, corpus_bucket, corpus_prefix, output_format, max_samples } = input;
  if (!persona_id) throw new Error('缺少 persona_id');

  const bucket = corpus_bucket || DEFAULT_BUCKET;
  const prefix = corpus_prefix || 'tcs-structured/';
  const format = output_format || 'jsonl';
  const maxSamples = max_samples || MAX_SAMPLES_DEFAULT;
  const datasetId = `ds-${persona_id}-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;

  // 扫描TCS语料文件
  let corpusFiles = [];
  try {
    const result = await cos.list(bucket, prefix, 500);
    corpusFiles = result.files.filter(f => f.key.endsWith('.tcs.json') || f.key.endsWith('.json'));
  } catch {
    throw new Error(`无法读取语料桶 ${bucket}/${prefix}`);
  }

  if (corpusFiles.length === 0) {
    throw new Error(`语料桶 ${bucket}/${prefix} 中未找到TCS语料文件`);
  }

  // 逐文件读取并转换为JSONL三元组
  const jsonlLines = [];
  let filesProcessed = 0;

  for (const file of corpusFiles) {
    if (jsonlLines.length >= maxSamples) break;

    try {
      const raw = await cos.read(bucket, file.key);
      const corpus = JSON.parse(raw.content);
      const entries = corpus.entries || (Array.isArray(corpus) ? corpus : [corpus]);

      for (const entry of entries) {
        if (jsonlLines.length >= maxSamples) break;

        const triple = convertEntryToTriple(persona_id, corpus.corpus_type, entry);
        if (triple) {
          jsonlLines.push(JSON.stringify(triple));
        }
      }
      filesProcessed++;
    } catch {
      // 跳过无法解析的文件
    }
  }

  if (jsonlLines.length === 0) {
    throw new Error('未能从语料中生成任何训练样本');
  }

  // 写入JSONL到COS
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const fileKey = `finetune-datasets/${persona_id}/${timestamp}.${format}`;
  const jsonlContent = jsonlLines.join('\n') + '\n';

  await cos.write(bucket, fileKey, jsonlContent, 'application/jsonl');

  return {
    dataset_id: datasetId,
    file_key: fileKey,
    sample_count: jsonlLines.length,
    format,
    files_scanned: corpusFiles.length,
    files_processed: filesProcessed,
    bucket,
    created_at: new Date().toISOString()
  };
}

/**
 * finetuneSubmitJob — 提交微调任务到DeepSeek或Qwen API
 *
 * 从COS读取JSONL数据集，上传到provider，然后创建微调任务
 *
 * input:
 *   persona_id: string    — 人格体ID
 *   dataset_key: string   — COS中JSONL文件路径
 *   provider: string      — 微调提供方（deepseek / qwen）
 *   base_model: string    — 基座模型（可选，默认取provider默认值）
 *   hyperparams: object   — 超参数（可选）
 */
async function finetuneSubmitJob(input) {
  const { persona_id, dataset_key, provider, base_model, hyperparams } = input;
  if (!persona_id) throw new Error('缺少 persona_id');
  if (!dataset_key) throw new Error('缺少 dataset_key');

  const providerKey = (provider || 'deepseek').toLowerCase();
  const providerConfig = FINETUNE_PROVIDERS[providerKey];
  if (!providerConfig) throw new Error(`不支持的微调提供方: ${providerKey}，仅支持 deepseek / qwen`);

  const apiKey = process.env[providerConfig.keyEnv];
  if (!apiKey) throw new Error(`缺少API密钥环境变量 ${providerConfig.keyEnv}`);

  const jobId = `ft-${persona_id}-${Date.now()}-${crypto.randomBytes(4).toString('hex')}`;
  const model = base_model || providerConfig.defaultModel;

  // 从COS读取JSONL数据集
  const bucket = DEFAULT_BUCKET;
  const raw = await cos.read(bucket, dataset_key);
  const datasetContent = raw.content;

  // 上传训练文件到provider
  const fileId = await uploadTrainingFile(providerConfig, apiKey, datasetContent, providerKey);

  // 创建微调任务
  const jobResult = await createFinetuneJob(providerConfig, apiKey, model, fileId, hyperparams, providerKey);

  // 保存任务元数据到COS
  const jobMeta = {
    job_id: jobId,
    provider_job_id: jobResult.provider_job_id,
    persona_id,
    provider: providerKey,
    base_model: model,
    dataset_key,
    file_id: fileId,
    hyperparams: hyperparams || {},
    status: jobResult.status || 'pending',
    created_at: new Date().toISOString(),
    updated_at: new Date().toISOString()
  };

  await cos.write(bucket, `finetune-jobs/${persona_id}/${jobId}.json`,
    JSON.stringify(jobMeta, null, 2), 'application/json');

  return {
    job_id: jobId,
    provider_job_id: jobResult.provider_job_id,
    provider: providerKey,
    status: jobMeta.status,
    base_model: model,
    estimated_time: jobResult.estimated_time || '未知，通常需要数小时'
  };
}

/**
 * finetuneCheckStatus — 查询微调任务进度
 *
 * input:
 *   persona_id: string — 人格体ID
 *   job_id: string     — 任务ID
 *   provider: string   — 微调提供方
 */
async function finetuneCheckStatus(input) {
  const { persona_id, job_id, provider } = input;
  if (!persona_id) throw new Error('缺少 persona_id');
  if (!job_id) throw new Error('缺少 job_id');

  const bucket = DEFAULT_BUCKET;

  // 读取任务元数据
  let jobMeta;
  try {
    const raw = await cos.read(bucket, `finetune-jobs/${persona_id}/${job_id}.json`);
    jobMeta = JSON.parse(raw.content);
  } catch {
    throw new Error(`未找到微调任务: ${job_id}`);
  }

  const providerKey = provider || jobMeta.provider;
  const providerConfig = FINETUNE_PROVIDERS[providerKey];
  if (!providerConfig) throw new Error(`不支持的微调提供方: ${providerKey}`);

  const apiKey = process.env[providerConfig.keyEnv];
  if (!apiKey) throw new Error(`缺少API密钥环境变量 ${providerConfig.keyEnv}`);

  // 查询provider API获取最新状态
  const providerJobId = jobMeta.provider_job_id;
  let statusResult;
  try {
    statusResult = await queryJobStatus(providerConfig, apiKey, providerJobId, providerKey);
  } catch (err) {
    return {
      job_id,
      provider: providerKey,
      status: jobMeta.status,
      progress: null,
      metrics: null,
      error: `查询provider状态失败: ${err.message}`,
      last_known_update: jobMeta.updated_at
    };
  }

  // 更新COS中的任务元数据
  jobMeta.status = statusResult.status;
  jobMeta.updated_at = new Date().toISOString();
  if (statusResult.fine_tuned_model) {
    jobMeta.fine_tuned_model = statusResult.fine_tuned_model;
  }
  if (statusResult.metrics) {
    jobMeta.metrics = statusResult.metrics;
  }

  try {
    await cos.write(bucket, `finetune-jobs/${persona_id}/${job_id}.json`,
      JSON.stringify(jobMeta, null, 2), 'application/json');
  } catch { /* ignore */ }

  return {
    job_id,
    provider_job_id: providerJobId,
    provider: providerKey,
    status: statusResult.status,
    progress: statusResult.progress || null,
    metrics: statusResult.metrics || null,
    fine_tuned_model: statusResult.fine_tuned_model || null,
    updated_at: jobMeta.updated_at
  };
}

/**
 * finetuneRegisterModel — 注册微调完成的模型
 *
 * input:
 *   persona_id: string      — 人格体ID
 *   job_id: string          — 关联的微调任务ID
 *   model_endpoint: string  — 模型推理端点（provider返回的fine_tuned_model名称）
 *   model_name: string      — 本地注册名称
 *   provider: string        — 微调提供方
 *   description: string     — 模型描述
 */
async function finetuneRegisterModel(input) {
  const { persona_id, job_id, model_endpoint, model_name, provider, description } = input;
  if (!persona_id) throw new Error('缺少 persona_id');
  if (!model_endpoint) throw new Error('缺少 model_endpoint');
  if (!model_name) throw new Error('缺少 model_name');

  const providerKey = (provider || 'deepseek').toLowerCase();
  const providerConfig = FINETUNE_PROVIDERS[providerKey];
  if (!providerConfig) throw new Error(`不支持的微调提供方: ${providerKey}`);

  const modelId = `mdl-${persona_id}-${crypto.randomBytes(4).toString('hex')}`;
  const now = new Date().toISOString();

  const modelConfig = {
    model_id: modelId,
    persona_id,
    model_name,
    model_endpoint,
    provider: providerKey,
    provider_host: providerConfig.host,
    inference_path: providerConfig.inferencePath,
    key_env: providerConfig.keyEnv,
    job_id: job_id || null,
    description: description || `${persona_id} 微调模型`,
    status: 'active',
    created_at: now,
    updated_at: now
  };

  const bucket = DEFAULT_BUCKET;
  const configKey = `finetune-models/${persona_id}/${model_name}.json`;
  await cos.write(bucket, configKey, JSON.stringify(modelConfig, null, 2), 'application/json');

  return {
    model_id: modelId,
    model_name,
    provider: providerKey,
    registered_at: now,
    config_key: configKey,
    config: modelConfig
  };
}

/**
 * finetuneListModels — 列出已注册的微调模型
 *
 * input:
 *   persona_id: string — 人格体ID
 */
async function finetuneListModels(input) {
  const { persona_id } = input;
  if (!persona_id) throw new Error('缺少 persona_id');

  const bucket = DEFAULT_BUCKET;
  const prefix = `finetune-models/${persona_id}/`;

  let files = [];
  try {
    const result = await cos.list(bucket, prefix, 100);
    files = result.files.filter(f => f.key.endsWith('.json'));
  } catch {
    return { persona_id, models: [], count: 0 };
  }

  const models = [];
  for (const file of files) {
    try {
      const raw = await cos.read(bucket, file.key);
      const config = JSON.parse(raw.content);
      models.push({
        model_name: config.model_name,
        model_id: config.model_id,
        provider: config.provider,
        model_endpoint: config.model_endpoint,
        status: config.status,
        description: config.description,
        created_at: config.created_at
      });
    } catch {
      // 跳过无法解析的配置
    }
  }

  return {
    persona_id,
    models,
    count: models.length
  };
}

/**
 * finetuneCallModel — 调用微调模型进行推理
 *
 * 加载模型配置，调用provider推理API
 * 微调模型不可用时自动降级到基座模型
 *
 * input:
 *   persona_id: string   — 人格体ID
 *   model_name: string   — 已注册的模型名称
 *   prompt: string       — 推理提示词
 *   temperature: number  — 温度（默认0.7）
 *   max_tokens: number   — 最大token数（默认1000）
 */
async function finetuneCallModel(input) {
  const { persona_id, model_name, prompt, temperature, max_tokens } = input;
  if (!persona_id) throw new Error('缺少 persona_id');
  if (!model_name) throw new Error('缺少 model_name');
  if (!prompt) throw new Error('缺少 prompt');

  const bucket = DEFAULT_BUCKET;
  let modelConfig;
  let fallbackUsed = false;

  // 加载模型配置
  try {
    const raw = await cos.read(bucket, `finetune-models/${persona_id}/${model_name}.json`);
    modelConfig = JSON.parse(raw.content);
  } catch {
    throw new Error(`未找到模型配置: ${model_name}`);
  }

  const apiKey = process.env[modelConfig.key_env];
  if (!apiKey) throw new Error(`缺少API密钥环境变量 ${modelConfig.key_env}`);

  const temp = typeof temperature === 'number' ? temperature : 0.7;
  const tokens = max_tokens || 1000;

  // 尝试调用微调模型
  try {
    const result = await callInferenceAPI({
      host: modelConfig.provider_host,
      path: modelConfig.inference_path,
      model: modelConfig.model_endpoint
    }, apiKey, prompt, temp, tokens);

    return {
      response: result.content,
      model_used: modelConfig.model_endpoint,
      provider: modelConfig.provider,
      tokens: result.tokens,
      fallback_used: false
    };
  } catch {
    // 微调模型不可用，降级到基座模型
    fallbackUsed = true;
  }

  // 降级：使用基座模型
  const baseConfig = LLM_CONFIGS[modelConfig.provider === 'deepseek' ? 'deepseek-chat' : 'qwen-max'];
  if (!baseConfig) throw new Error('降级失败：无可用基座模型');

  const baseKey = process.env[baseConfig.keyEnv];
  if (!baseKey) throw new Error(`降级失败：缺少基座模型API密钥 ${baseConfig.keyEnv}`);

  const result = await callInferenceAPI({
    host: baseConfig.host,
    path: baseConfig.path,
    model: baseConfig.model
  }, baseKey, prompt, temp, tokens);

  return {
    response: result.content,
    model_used: baseConfig.model,
    provider: modelConfig.provider,
    tokens: result.tokens,
    fallback_used: fallbackUsed,
    fallback_reason: '微调模型不可用，已降级到基座模型'
  };
}

/**
 * finetuneCompareModels — A/B测试微调 vs 基座模型
 *
 * 用相同的prompt分别调用微调模型和基座模型，返回对比结果
 *
 * input:
 *   persona_id: string  — 人格体ID
 *   model_name: string  — 已注册的微调模型名称
 *   test_prompt: string — 测试提示词
 *   base_model: string  — 基座模型名称（默认取provider对应的基座）
 */
async function finetuneCompareModels(input) {
  const { persona_id, model_name, test_prompt, base_model } = input;
  if (!persona_id) throw new Error('缺少 persona_id');
  if (!model_name) throw new Error('缺少 model_name');
  if (!test_prompt) throw new Error('缺少 test_prompt');

  const bucket = DEFAULT_BUCKET;

  // 加载微调模型配置
  let modelConfig;
  try {
    const raw = await cos.read(bucket, `finetune-models/${persona_id}/${model_name}.json`);
    modelConfig = JSON.parse(raw.content);
  } catch {
    throw new Error(`未找到模型配置: ${model_name}`);
  }

  const apiKey = process.env[modelConfig.key_env];
  if (!apiKey) throw new Error(`缺少API密钥环境变量 ${modelConfig.key_env}`);

  // 确定基座模型
  const baseModelName = base_model || (modelConfig.provider === 'deepseek' ? 'deepseek-chat' : 'qwen-max');
  const baseConfig = LLM_CONFIGS[baseModelName];
  if (!baseConfig) throw new Error(`未找到基座模型配置: ${baseModelName}`);

  const baseKey = process.env[baseConfig.keyEnv];
  if (!baseKey) throw new Error(`缺少基座模型API密钥 ${baseConfig.keyEnv}`);

  // 并行调用两个模型
  const [finetunedResult, baseResult] = await Promise.allSettled([
    callInferenceAPI({
      host: modelConfig.provider_host,
      path: modelConfig.inference_path,
      model: modelConfig.model_endpoint
    }, apiKey, test_prompt, 0.7, 1000),
    callInferenceAPI({
      host: baseConfig.host,
      path: baseConfig.path,
      model: baseConfig.model
    }, baseKey, test_prompt, 0.7, 1000)
  ]);

  return {
    test_prompt,
    finetuned_response: finetunedResult.status === 'fulfilled'
      ? finetunedResult.value.content
      : `调用失败: ${finetunedResult.reason?.message || '未知错误'}`,
    base_response: baseResult.status === 'fulfilled'
      ? baseResult.value.content
      : `调用失败: ${baseResult.reason?.message || '未知错误'}`,
    model_a: {
      name: modelConfig.model_endpoint,
      type: 'finetuned',
      tokens: finetunedResult.status === 'fulfilled' ? finetunedResult.value.tokens : null
    },
    model_b: {
      name: baseConfig.model,
      type: 'base',
      tokens: baseResult.status === 'fulfilled' ? baseResult.value.tokens : null
    },
    compared_at: new Date().toISOString()
  };
}

/**
 * finetuneGetCostEstimate — 估算微调成本
 *
 * 读取JSONL数据集，统计token数量，按provider定价估算费用
 *
 * input:
 *   persona_id: string  — 人格体ID
 *   dataset_key: string — COS中JSONL文件路径
 *   provider: string    — 微调提供方（deepseek / qwen）
 */
async function finetuneGetCostEstimate(input) {
  const { persona_id, dataset_key, provider } = input;
  if (!persona_id) throw new Error('缺少 persona_id');
  if (!dataset_key) throw new Error('缺少 dataset_key');

  const bucket = DEFAULT_BUCKET;
  const providerKey = (provider || 'deepseek').toLowerCase();

  if (!FINETUNE_PROVIDERS[providerKey]) {
    throw new Error(`不支持的微调提供方: ${providerKey}`);
  }

  // 读取JSONL数据集
  const raw = await cos.read(bucket, dataset_key);
  const lines = raw.content.split('\n').filter(l => l.trim());

  // 统计token（中文约每字1.5-2 token，英文约每词1 token，粗估用字符数/2）
  let totalChars = 0;
  let sampleCount = 0;

  for (const line of lines) {
    try {
      const sample = JSON.parse(line);
      const messages = sample.messages || [];
      for (const msg of messages) {
        totalChars += (msg.content || '').length;
      }
      sampleCount++;
    } catch {
      // 跳过无效行
    }
  }

  // 粗估token数（中文字符 ≈ 1.5 tokens，英文约1:1）
  const estimatedTokens = Math.ceil(totalChars * 1.5);
  const costPer1k = COST_PER_1K_TOKENS[providerKey] || 0.02;

  // 微调通常跑 3-4 个 epoch
  const epochs = 3;
  const totalTrainTokens = estimatedTokens * epochs;
  const estimatedCostRmb = (totalTrainTokens / 1000) * costPer1k;

  return {
    dataset_key,
    sample_count: sampleCount,
    total_chars: totalChars,
    token_count: estimatedTokens,
    training_tokens: totalTrainTokens,
    epochs,
    estimated_cost_rmb: Math.round(estimatedCostRmb * 100) / 100,
    provider: providerKey,
    cost_per_1k_tokens: costPer1k,
    notes: [
      `Token估算基于字符数粗估（中文 ×1.5），实际以provider计费为准`,
      `训练按 ${epochs} 个epoch估算`,
      `${FINETUNE_PROVIDERS[providerKey].label} 当前参考价: ¥${costPer1k}/1K tokens`,
      `实际费用可能因模型版本和优惠策略有所不同`
    ]
  };
}

// ═══════════════════════════════════════════════════════════
// Provider API 交互（内部实现）
// ═══════════════════════════════════════════════════════════

/**
 * 上传训练文件到provider
 */
function uploadTrainingFile(providerConfig, apiKey, content, providerKey) {
  return new Promise((resolve, reject) => {
    // 构建 multipart/form-data
    const boundary = `----FormBoundary${crypto.randomBytes(8).toString('hex')}`;
    const fileName = `training-${Date.now()}.jsonl`;

    let bodyParts = [];
    bodyParts.push(`--${boundary}\r\n`);
    bodyParts.push(`Content-Disposition: form-data; name="purpose"\r\n\r\n`);
    bodyParts.push(`fine-tune\r\n`);
    bodyParts.push(`--${boundary}\r\n`);
    bodyParts.push(`Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`);
    bodyParts.push(`Content-Type: application/jsonl\r\n\r\n`);
    bodyParts.push(content);
    bodyParts.push(`\r\n--${boundary}--\r\n`);

    const body = bodyParts.join('');

    const req = https.request({
      hostname: providerConfig.host,
      port: 443,
      path: providerConfig.uploadPath,
      method: 'POST',
      headers: {
        'Content-Type': `multipart/form-data; boundary=${boundary}`,
        'Authorization': `Bearer ${apiKey}`,
        'Content-Length': Buffer.byteLength(body)
      },
      timeout: FINETUNE_TIMEOUT
    }, (res) => {
      const chunks = [];
      res.on('data', c => chunks.push(c));
      res.on('end', () => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          try {
            const data = JSON.parse(Buffer.concat(chunks).toString());
            // DeepSeek返回 {id: "file-xxx"}, Qwen返回类似结构
            resolve(data.id || data.file_id || data.output?.file_id || '');
          } catch {
            reject(new Error('训练文件上传响应解析失败'));
          }
        } else {
          reject(new Error(`训练文件上传失败: HTTP ${res.statusCode}`));
        }
      });
    });

    req.on('error', reject);
    req.on('timeout', () => { req.destroy(); reject(new Error('训练文件上传超时')); });
    req.write(body);
    req.end();
  });
}

/**
 * 创建微调任务
 */
function createFinetuneJob(providerConfig, apiKey, model, fileId, hyperparams, providerKey) {
  return new Promise((resolve, reject) => {
    let requestBody;

    if (providerKey === 'deepseek') {
      requestBody = {
        model,
        training_file: fileId,
        hyperparameters: {
          n_epochs: hyperparams?.n_epochs || 3,
          learning_rate_multiplier: hyperparams?.learning_rate_multiplier || 1.0,
          batch_size: hyperparams?.batch_size || 'auto'
        }
      };
    } else {
      // Qwen/DashScope 格式
      requestBody = {
        model,
        training_file_ids: [fileId],
        hyper_parameters: {
          n_epochs: hyperparams?.n_epochs || 3,
          learning_rate: hyperparams?.learning_rate_multiplier || 1e-5,
          batch_size: hyperparams?.batch_size || 4
        }
      };
    }

    const body = JSON.stringify(requestBody);

    const req = https.request({
      hostname: providerConfig.host,
      port: 443,
      path: providerConfig.createPath,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${apiKey}`,
        'Content-Length': Buffer.byteLength(body)
      },
      timeout: FINETUNE_TIMEOUT
    }, (res) => {
      const chunks = [];
      res.on('data', c => chunks.push(c));
      res.on('end', () => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          try {
            const data = JSON.parse(Buffer.concat(chunks).toString());
            resolve({
              provider_job_id: data.id || data.output?.job_id || '',
              status: data.status || data.output?.status || 'pending',
              estimated_time: data.estimated_completion || null
            });
          } catch {
            reject(new Error('微调任务创建响应解析失败'));
          }
        } else {
          reject(new Error(`微调任务创建失败: HTTP ${res.statusCode}`));
        }
      });
    });

    req.on('error', reject);
    req.on('timeout', () => { req.destroy(); reject(new Error('微调任务创建超时')); });
    req.write(body);
    req.end();
  });
}

/**
 * 查询微调任务状态
 */
function queryJobStatus(providerConfig, apiKey, providerJobId, providerKey) {
  return new Promise((resolve, reject) => {
    const path = `${providerConfig.statusPath}${encodeURIComponent(providerJobId)}`;

    const req = https.request({
      hostname: providerConfig.host,
      port: 443,
      path,
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${apiKey}`
      },
      timeout: 30000
    }, (res) => {
      const chunks = [];
      res.on('data', c => chunks.push(c));
      res.on('end', () => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          try {
            const data = JSON.parse(Buffer.concat(chunks).toString());

            // 统一不同provider的状态字段
            let status, progress, metrics, fineTunedModel;

            if (providerKey === 'deepseek') {
              status = data.status || 'unknown';
              fineTunedModel = data.fine_tuned_model || null;
              metrics = data.result_files ? { result_files: data.result_files } : null;
              progress = data.trained_tokens
                ? { trained_tokens: data.trained_tokens }
                : null;
            } else {
              // Qwen
              const output = data.output || data;
              status = output.status || data.status || 'unknown';
              fineTunedModel = output.fine_tuned_model || output.finetuned_output?.model_id || null;
              metrics = output.metrics || null;
              progress = output.training_progress || null;
            }

            // 统一状态值
            status = normalizeJobStatus(status);

            resolve({ status, progress, metrics, fine_tuned_model: fineTunedModel });
          } catch {
            reject(new Error('微调状态查询响应解析失败'));
          }
        } else {
          reject(new Error(`微调状态查询失败: HTTP ${res.statusCode}`));
        }
      });
    });

    req.on('error', reject);
    req.on('timeout', () => { req.destroy(); reject(new Error('微调状态查询超时')); });
    req.end();
  });
}

/**
 * 调用推理API（微调模型或基座模型通用）
 */
function callInferenceAPI(config, apiKey, prompt, temperature, maxTokens) {
  return new Promise((resolve, reject) => {
    const body = JSON.stringify({
      model: config.model,
      messages: [
        { role: 'user', content: prompt }
      ],
      temperature: temperature || 0.7,
      max_tokens: maxTokens || 1000
    });

    const req = https.request({
      hostname: config.host,
      port: 443,
      path: config.path,
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${apiKey}`,
        'Content-Length': Buffer.byteLength(body)
      },
      timeout: 30000
    }, (res) => {
      const chunks = [];
      res.on('data', c => chunks.push(c));
      res.on('end', () => {
        if (res.statusCode >= 200 && res.statusCode < 300) {
          try {
            const data = JSON.parse(Buffer.concat(chunks).toString());
            resolve({
              content: data.choices?.[0]?.message?.content || '',
              tokens: data.usage || {}
            });
          } catch {
            reject(new Error('推理响应解析失败'));
          }
        } else {
          reject(new Error(`推理调用失败: HTTP ${res.statusCode}`));
        }
      });
    });

    req.on('error', reject);
    req.on('timeout', () => { req.destroy(); reject(new Error('推理请求超时')); });
    req.write(body);
    req.end();
  });
}

// ═══════════════════════════════════════════════════════════
// 辅助函数
// ═══════════════════════════════════════════════════════════

/**
 * 将TCS语料条目转换为微调JSONL三元组
 */
function convertEntryToTriple(personaId, corpusType, entry) {
  const content = typeof entry.content === 'string'
    ? entry.content
    : (entry.text || entry.summary || JSON.stringify(entry));

  if (!content || content.length < 10) return null;

  const tags = entry.tcs_tags || entry.tags || [];
  const category = entry.category || corpusType || 'general';

  // 生成system prompt（人格体身份）
  const systemContent = `你是${personaId}，光湖系统中的人格体。你的思维方式基于TCS语言核系统，` +
    `你需要以${personaId}的视角和风格来回答问题。`;

  // 根据语料类型构建instruction/input/output
  let userContent, assistantContent;

  if (entry.question && entry.answer) {
    // 已有Q&A结构
    userContent = entry.question;
    assistantContent = entry.answer;
  } else if (tags.length > 0) {
    // 有标签的条目：生成关于该内容的问答
    userContent = `关于${category}类型的内容，请解释以下要点: ${tags.slice(0, 3).join('、')}`;
    assistantContent = content;
  } else {
    // 通用条目：以理解和阐述的方式构建
    userContent = `请阐述你对以下内容的理解和看法：\n${content.substring(0, 200)}`;
    assistantContent = content;
  }

  return {
    messages: [
      { role: 'system', content: systemContent },
      { role: 'user', content: userContent },
      { role: 'assistant', content: assistantContent }
    ]
  };
}

/**
 * 统一不同provider的任务状态值
 */
function normalizeJobStatus(rawStatus) {
  const statusMap = {
    // DeepSeek 状态
    validating_files: 'pending',
    queued: 'pending',
    running: 'running',
    succeeded: 'completed',
    failed: 'failed',
    cancelled: 'failed',
    // Qwen/DashScope 状态
    PENDING: 'pending',
    RUNNING: 'running',
    SUCCEEDED: 'completed',
    FAILED: 'failed',
    CANCELED: 'failed',
    // 通用
    pending: 'pending',
    completed: 'completed'
  };

  return statusMap[rawStatus] || rawStatus;
}

module.exports = {
  finetuneExportDataset,
  finetuneSubmitJob,
  finetuneCheckStatus,
  finetuneRegisterModel,
  finetuneListModels,
  finetuneCallModel,
  finetuneCompareModels,
  finetuneGetCostEstimate
};
