/**
 * 光湖视频AI系统 · 阿里百炼万相 API 适配器
 * D140 · 铸渊 ICE-GL-ZY001 · 2026-06-23
 *
 * 阿里云百炼平台 · 万相2.7视频生成
 * 文档: https://help.aliyun.com/zh/model-studio/text-to-video-api-reference
 *
 * 支持能力:
 *   - 文生视频 (wan2.7-t2v): 纯文本prompt生成视频
 *   - 图生视频 (wan2.7-i2v): 首帧/首尾帧/音频驱动 + 文本生成视频
 *   - 视频续写: 首视频片段续写
 *
 * 使用方式:
 *   const { generateWanVideo, generateWanImageToVideo, queryWanTask } = require('./wan-api-adapter');
 *   const result = await generateWanVideo({ prompt: '一只小猫在月光下奔跑', duration: 5 });
 *
 * 环境变量（由 LOCAL-SECRETS-PATH.hdlp 的苍耳本机路径加载）:
 *   ALIYUN_BAILIAN_API_KEY=
 *   ALIYUN_BAILIAN_BASE_URL=https://dashscope.aliyuncs.com/api/v1  (默认)
 *   ALIYUN_BAILIAN_WORKSPACE_ID=  (可选·新加坡地域时需要)
 */

const fs = require('fs');
const path = require('path');
const https = require('https');
const http = require('http');
const { loadVideoAiEnv } = require('./env-loader');

loadVideoAiEnv(path.resolve(__dirname, '../.env'));

const API_KEY = process.env.ALIYUN_BAILIAN_API_KEY || '';
const WORKSPACE_ID = process.env.ALIYUN_BAILIAN_WORKSPACE_ID || '';
// 北京地域默认URL；新加坡地域需要 workspaceId
const BASE_URL = WORKSPACE_ID
  ? `https://${WORKSPACE_ID}.ap-southeast-1.maas.aliyuncs.com/api/v1`
  : (process.env.ALIYUN_BAILIAN_BASE_URL || 'https://dashscope.aliyuncs.com/api/v1');
const POLL_INTERVAL_MS = parseInt(process.env.WAN_POLL_INTERVAL_MS, 10) || 15000; // 万相建议15秒
const MAX_POLL_ATTEMPTS = parseInt(process.env.WAN_MAX_POLL_ATTEMPTS, 10) || 80; // 最多约20分钟

// 输出路径
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;
})();

// ==================== HTTP 工具 ====================

function httpRequest(method, urlStr, body, apiKey, extraHeaders = {}) {
  const urlObj = new URL(urlStr);
  const isHttps = urlObj.protocol === 'https:';
  const transport = isHttps ? https : http;
  const payload = body ? JSON.stringify(body) : null;

  return new Promise((resolve, reject) => {
    const headers = {
      'Authorization': `Bearer ${apiKey}`,
      'Content-Type': 'application/json',
      'X-DashScope-Async': 'enable',  // 万相必须设置异步头
      ...extraHeaders,
    };
    if (payload) headers['Content-Length'] = Buffer.byteLength(payload);

    const options = {
      hostname: urlObj.hostname,
      port: urlObj.port || (isHttps ? 443 : 80),
      path: urlObj.pathname + urlObj.search,
      method,
      headers,
      timeout: 30000,
    };

    const req = transport.request(options, (res) => {
      let data = '';
      res.on('data', chunk => data += chunk);
      res.on('end', () => {
        try {
          const json = JSON.parse(data);
          // 万相错误码在顶层: { code: "xxx", message: "xxx" }
          if (json.code && json.code !== 'Success' && res.statusCode >= 400) {
            reject(new Error(`万相API错误(${json.code}): ${json.message}`));
            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('请求超时')); });
    if (payload) req.write(payload);
    req.end();
  });
}

async function httpPost(url, body, apiKey) {
  return httpRequest('POST', url, body, apiKey);
}

async function httpGet(url, apiKey) {
  return httpRequest('GET', url, null, apiKey);
}

async function downloadFile(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}`;
        downloadFile(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 WAN_T2V_SPEC = {
  model: 'wan2.7-t2v',
  duration: { min: 2, max: 15, default: 5 },
  resolution: { values: ['720P', '1080P'], default: '1080P' },
  ratio: { values: ['16:9', '9:16', '1:1', '4:3', '3:4'], default: '16:9' },
  promptMaxLen: 5000,
};

const WAN_I2V_SPEC = {
  model: 'wan2.7-i2v-2026-04-25',
  duration: { min: 2, max: 15, default: 5 },
  resolution: { values: ['720P', '1080P'], default: '1080P' },
  promptMaxLen: 5000,
  // 图生视频的宽高比由输入素材决定，不需要单独传ratio
  mediaTypes: ['first_frame', 'last_frame', 'driving_audio', 'first_clip'],
};

// ==================== 预校验 ====================

function preflightCheckT2V({ prompt, duration, resolution, ratio }) {
  const warnings = [];
  const errors = [];
  const corrected = {};

  // prompt
  if (!prompt || !prompt.trim()) {
    errors.push('提示词不能为空');
  } else if (prompt.length > WAN_T2V_SPEC.promptMaxLen) {
    warnings.push(`提示词超过 ${WAN_T2V_SPEC.promptMaxLen} 字符，将自动截断`);
  }
  corrected.prompt = prompt;

  // duration
  const dur = parseInt(duration, 10);
  if (duration !== undefined && duration !== null) {
    if (isNaN(dur) || dur < WAN_T2V_SPEC.duration.min || dur > WAN_T2V_SPEC.duration.max) {
      errors.push(`duration 范围: ${WAN_T2V_SPEC.duration.min}~${WAN_T2V_SPEC.duration.max}秒，收到: ${duration}`);
    } else {
      corrected.duration = dur;
    }
  } else {
    corrected.duration = WAN_T2V_SPEC.duration.default;
  }

  // resolution
  if (!resolution || !WAN_T2V_SPEC.resolution.values.includes(resolution)) {
    if (resolution) warnings.push(`resolution "${resolution}" 不支持，修正为 ${WAN_T2V_SPEC.resolution.default}`);
    corrected.resolution = WAN_T2V_SPEC.resolution.default;
  } else {
    corrected.resolution = resolution;
  }

  // ratio
  if (!ratio || !WAN_T2V_SPEC.ratio.values.includes(ratio)) {
    if (ratio) warnings.push(`ratio "${ratio}" 不支持，修正为 ${WAN_T2V_SPEC.ratio.default}`);
    corrected.ratio = WAN_T2V_SPEC.ratio.default;
  } else {
    corrected.ratio = ratio;
  }

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

function preflightCheckI2V({ prompt, media, duration, resolution }) {
  const warnings = [];
  const errors = [];
  const corrected = {};

  // media必选
  if (!media || !Array.isArray(media) || media.length === 0) {
    errors.push('图生视频必须提供 media 素材（至少一个 first_frame）');
  } else {
    // 检查media格式
    for (const m of media) {
      if (!WAN_I2V_SPEC.mediaTypes.includes(m.type)) {
        errors.push(`media type "${m.type}" 不支持，可选: ${WAN_I2V_SPEC.mediaTypes.join(', ')}`);
      }
      if (!m.url) {
        errors.push(`media ${m.type} 缺少 url`);
      }
    }
    corrected.media = media;
  }

  // prompt可选
  corrected.prompt = prompt || '';

  // duration
  const dur = parseInt(duration, 10);
  if (duration !== undefined && duration !== null) {
    if (isNaN(dur) || dur < WAN_I2V_SPEC.duration.min || dur > WAN_I2V_SPEC.duration.max) {
      errors.push(`duration 范围: ${WAN_I2V_SPEC.duration.min}~${WAN_I2V_SPEC.duration.max}秒`);
    } else {
      corrected.duration = dur;
    }
  } else {
    corrected.duration = WAN_I2V_SPEC.duration.default;
  }

  // resolution
  if (!resolution || !WAN_I2V_SPEC.resolution.values.includes(resolution)) {
    if (resolution) warnings.push(`resolution "${resolution}" 不支持，修正为 ${WAN_I2V_SPEC.resolution.default}`);
    corrected.resolution = WAN_I2V_SPEC.resolution.default;
  } else {
    corrected.resolution = resolution;
  }

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

// ==================== 提交任务 ====================

const SUBMIT_PATH = '/services/aigc/video-generation/video-synthesis';
const QUERY_PATH = '/tasks/';

/**
 * 提交万相文生视频任务
 * @param {object} opts
 * @param {string} opts.prompt - 视频描述提示词
 * @param {number} [opts.duration] - 时长 2-15秒，默认5
 * @param {string} [opts.resolution] - '720P' | '1080P'，默认1080P
 * @param {string} [opts.ratio] - '16:9' 等，默认16:9
 * @param {string} [opts.negativePrompt] - 反向提示词
 * @param {boolean} [opts.promptExtend=true] - 是否智能改写prompt
 * @param {boolean} [opts.watermark=false] - 是否加水印
 * @param {number} [opts.seed] - 随机种子
 * @returns {Promise<{taskId: string, preflight: object}>}
 */
async function submitT2VTask({ prompt, duration, resolution, ratio, negativePrompt, promptExtend, watermark, seed }) {
  const preflight = preflightCheckT2V({ prompt, duration, resolution, ratio });

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

  if (preflight.warnings.length > 0) {
    console.warn('[Wan·预校验] ⚠️', preflight.warnings.join('; '));
  }

  const c = preflight.corrected;
  const body = {
    model: WAN_T2V_SPEC.model,
    input: {
      prompt: c.prompt,
    },
    parameters: {
      resolution: c.resolution,
      ratio: c.ratio,
      duration: c.duration,
      prompt_extend: promptExtend !== false,
      watermark: watermark === true,
    },
  };

  if (negativePrompt) body.input.negative_prompt = negativePrompt;
  if (seed !== undefined) body.parameters.seed = seed;

  console.log(`[Wan·T2V] 提交: "${c.prompt.substring(0, 60)}..." ${c.duration}s ${c.resolution} ${c.ratio}`);

  const url = `${BASE_URL}${SUBMIT_PATH}`;
  const data = await httpPost(url, body, API_KEY);

  const taskId = data.output?.task_id;
  if (!taskId) {
    throw new Error(`万相未返回task_id: ${JSON.stringify(data).substring(0, 300)}`);
  }

  console.log(`[Wan·T2V] 任务ID: ${taskId}  状态: ${data.output?.task_status}`);
  return { taskId, preflight };
}

/**
 * 提交万相图生视频任务
 * @param {object} opts
 * @param {string} [opts.prompt] - 辅助文本描述
 * @param {Array} opts.media - 媒体素材 [{ type: 'first_frame', url: '...' }, ...]
 *   type可选: first_frame, last_frame, driving_audio, first_clip
 *   url支持: 公网URL / oss://临时URL / data:image/png;base64,...
 * @param {number} [opts.duration] - 时长 2-15秒
 * @param {string} [opts.resolution] - '720P' | '1080P'
 * @param {string} [opts.negativePrompt] - 反向提示词
 * @param {boolean} [opts.promptExtend=true] - 智能改写
 * @param {boolean} [opts.watermark=false] - 加水印
 * @param {number} [opts.seed] - 随机种子
 * @returns {Promise<{taskId: string, preflight: object}>}
 */
async function submitI2VTask({ prompt, media, duration, resolution, negativePrompt, promptExtend, watermark, seed }) {
  // 支持本地图片路径自动转base64
  const processedMedia = media.map(m => {
    if (m.url && fs.existsSync(m.url)) {
      // 本地文件 → 转base64
      const imgBuffer = fs.readFileSync(m.url);
      const ext = path.extname(m.url).toLowerCase();
      const mimeMap = { '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg', '.webp': 'image/webp', '.bmp': 'image/bmp' };
      const mime = mimeMap[ext] || 'image/png';
      const b64 = imgBuffer.toString('base64');
      console.log(`[Wan·I2V] 本地素材已转base64: ${path.basename(m.url)} (${(imgBuffer.length/1024).toFixed(0)}KB)`);
      return { type: m.type, url: `data:${mime};base64,${b64}` };
    }
    return m;
  });

  const preflight = preflightCheckI2V({ prompt, media: processedMedia, duration, resolution });

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

  if (preflight.warnings.length > 0) {
    console.warn('[Wan·预校验] ⚠️', preflight.warnings.join('; '));
  }

  const c = preflight.corrected;
  const body = {
    model: WAN_I2V_SPEC.model,
    input: {
      prompt: c.prompt,
      media: c.media,
    },
    parameters: {
      resolution: c.resolution,
      duration: c.duration,
      prompt_extend: promptExtend !== false,
      watermark: watermark === true,
    },
  };

  if (negativePrompt) body.input.negative_prompt = negativePrompt;
  if (seed !== undefined) body.parameters.seed = seed;

  const mediaTypes = c.media.map(m => m.type).join('+');
  console.log(`[Wan·I2V] 提交: ${mediaTypes} + "${c.prompt.substring(0, 40)}..." ${c.duration}s ${c.resolution}`);

  const url = `${BASE_URL}${SUBMIT_PATH}`;
  const data = await httpPost(url, body, API_KEY);

  const taskId = data.output?.task_id;
  if (!taskId) {
    throw new Error(`万相未返回task_id: ${JSON.stringify(data).substring(0, 300)}`);
  }

  console.log(`[Wan·I2V] 任务ID: ${taskId}  状态: ${data.output?.task_status}`);
  return { taskId, preflight };
}

// ==================== 查询任务 ====================

/**
 * 查询万相任务状态
 * @param {string} taskId
 * @returns {Promise<{status: 'generating'|'completed'|'failed', videoUrl?: string, videoMeta?: object, rawResponse?: object, error?: string}>}
 */
async function queryWanTask(taskId) {
  const url = `${BASE_URL}${QUERY_PATH}${taskId}`;
  const data = await httpGet(url, API_KEY);

  const taskStatus = (data.output?.task_status || '').toUpperCase();

  if (taskStatus === 'SUCCEEDED') {
    const videoUrl = data.output?.video_url;
    if (!videoUrl) {
      return { status: 'failed', error: '任务成功但未返回视频URL', rawResponse: data };
    }

    const videoMeta = {};
    if (data.usage) {
      if (data.usage.duration !== undefined) videoMeta.duration = data.usage.duration;
      if (data.usage.output_video_duration !== undefined) videoMeta.outputDuration = data.usage.output_video_duration;
      if (data.usage.SR !== undefined) videoMeta.resolutionTier = data.usage.SR;
      if (data.usage.ratio !== undefined) videoMeta.ratio = data.usage.ratio;
      if (data.usage.video_count !== undefined) videoMeta.videoCount = data.usage.video_count;
    }
    if (data.output?.orig_prompt) videoMeta.origPrompt = data.output.orig_prompt;

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

  if (taskStatus === 'FAILED') {
    const errMsg = data.output?.message || data.message || '生成失败';
    const errCode = data.output?.code || data.code || '';
    return { status: 'failed', error: `${errCode}: ${errMsg}`, rawResponse: data };
  }

  if (taskStatus === 'CANCELED') {
    return { status: 'failed', error: '任务被取消', rawResponse: data };
  }

  if (taskStatus === 'UNKNOWN') {
    return { status: 'failed', error: 'task_id不存在或已过期(24h)', rawResponse: data };
  }

  // PENDING / RUNNING
  return { status: 'generating', rawResponse: data };
}

// ==================== 生成视频（完整流程）====================

/**
 * 万相文生视频 — 提交 + 轮询 + 下载
 * @param {object} opts
 * @param {string} opts.prompt - 视频提示词
 * @param {number} [opts.duration=5] - 时长 2-15秒
 * @param {string} [opts.resolution='1080P'] - 分辨率
 * @param {string} [opts.ratio='16:9'] - 宽高比
 * @param {string} [opts.negativePrompt] - 反向提示词
 * @param {string} [opts.outputPath] - 输出路径
 * @param {string} [opts.shotId] - 镜编号
 * @param {string} [opts.projectKey] - 项目标识
 * @returns {Promise<{videoPath: string, taskId: string, videoMeta: object, preflight: object}>}
 */
async function generateWanVideo(opts) {
  if (!API_KEY) {
    throw new Error('未配置 ALIYUN_BAILIAN_API_KEY。请按 LOCAL-SECRETS-PATH.hdlp 在苍耳本机仓库外配置。');
  }

  const { taskId, preflight } = await submitT2VTask(opts);

  // 轮询
  for (let i = 1; i <= MAX_POLL_ATTEMPTS; i++) {
    await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
    const result = await queryWanTask(taskId);

    if (result.status === 'completed') {
      // 解析输出路径
      let finalPath;
      if (opts.outputPath) {
        finalPath = opts.outputPath;
      } else if (opts.shotId && opts.projectKey) {
        const dir = path.join(VIDEO_OUTPUT_ROOT, opts.projectKey);
        if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
        finalPath = path.join(dir, `${opts.shotId}-wan.mp4`);
      } else {
        finalPath = path.join(VIDEO_OUTPUT_ROOT, `wan-${taskId}.mp4`);
      }

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

      if (result.videoMeta && Object.keys(result.videoMeta).length > 0) {
        console.log(`[Wan] 元数据:`, JSON.stringify(result.videoMeta));
      }

      return { videoPath: finalPath, taskId, videoMeta: result.videoMeta || {}, preflight };
    }

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

    if (i % 5 === 0) console.log(`[Wan] 生成中... (${i}/${MAX_POLL_ATTEMPTS}, ${i * POLL_INTERVAL_MS / 1000}s)`);
  }

  throw new Error(`万相轮询超时(${MAX_POLL_ATTEMPTS * POLL_INTERVAL_MS / 1000}s)`);
}

/**
 * 万相图生视频 — 提交 + 轮询 + 下载
 * @param {object} opts - 同 submitI2VTask 参数 + outputPath/shotId/projectKey
 * @returns {Promise<{videoPath: string, taskId: string, videoMeta: object, preflight: object}>}
 */
async function generateWanImageToVideo(opts) {
  if (!API_KEY) {
    throw new Error('未配置 ALIYUN_BAILIAN_API_KEY。请按 LOCAL-SECRETS-PATH.hdlp 在苍耳本机仓库外配置。');
  }

  const { taskId, preflight } = await submitI2VTask(opts);

  // 轮询
  for (let i = 1; i <= MAX_POLL_ATTEMPTS; i++) {
    await new Promise(r => setTimeout(r, POLL_INTERVAL_MS));
    const result = await queryWanTask(taskId);

    if (result.status === 'completed') {
      let finalPath;
      if (opts.outputPath) {
        finalPath = opts.outputPath;
      } else if (opts.shotId && opts.projectKey) {
        const dir = path.join(VIDEO_OUTPUT_ROOT, opts.projectKey);
        if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
        finalPath = path.join(dir, `${opts.shotId}-wan-i2v.mp4`);
      } else {
        finalPath = path.join(VIDEO_OUTPUT_ROOT, `wan-i2v-${taskId}.mp4`);
      }

      console.log(`[Wan·I2V] 生成完成！下载到: ${finalPath}`);
      await downloadFile(result.videoUrl, finalPath);
      console.log(`[Wan·I2V] ✅ 视频已保存: ${finalPath}`);

      return { videoPath: finalPath, taskId, videoMeta: result.videoMeta || {}, preflight };
    }

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

    if (i % 5 === 0) console.log(`[Wan·I2V] 生成中... (${i}/${MAX_POLL_ATTEMPTS})`);
  }

  throw new Error(`万相I2V轮询超时(${MAX_POLL_ATTEMPTS * POLL_INTERVAL_MS / 1000}s)`);
}

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

module.exports = {
  submitT2VTask,
  submitI2VTask,
  queryWanTask,
  generateWanVideo,
  generateWanImageToVideo,
  preflightCheckT2V,
  preflightCheckI2V,
  downloadFile,
  WAN_T2V_SPEC,
  WAN_I2V_SPEC,
  BASE_URL,
  API_KEY,  // 用于检测是否已配置
};
