import axios, { AxiosInstance } from 'axios';
import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import { spawn } from 'child_process';
import logger from '../log/main';
import { getElectronSystemConfigSync, fetchElectronSystemConfig } from '../systemConfig';
import { getFFmpegExecutablePath, resourceExists } from '../../lib/resource-path';

/**
 * RunningHub API 配置
 */
export interface RunningHubConfig {
  apiKey: string;
  workflowId: string;
  baseUrl?: string;
  apiVersion?: 'v1' | 'v2';
  instanceType?: 'default' | 'plus';
  nodes?: {
    videoNode: string;
    audioNode: string;
    durationNode?: string;
    fpsNode?: string;
    widthNode?: string;
    heightNode?: string;
  };
}

function runProcess(command: string, args: string[]): Promise<void> {
  return new Promise((resolve, reject) => {
    const proc = spawn(command, args, { windowsHide: true });
    let stderr = '';

    proc.stderr.on('data', (data: Buffer) => {
      stderr += data.toString();
    });
    proc.on('close', (code) => {
      if (code === 0) {
        resolve();
        return;
      }
      reject(new Error(stderr || `process exited with code ${code}`));
    });
    proc.on('error', reject);
  });
}

async function normalizeAudioForRunningHubUpload(filePath: string): Promise<{ filePath: string; cleanupPath?: string }> {
  const ffmpegPath = getFFmpegExecutablePath();
  if (!resourceExists(ffmpegPath)) {
    logger.warn('[RunningHub] FFmpeg not found, upload original audio file', { filePath, ffmpegPath });
    return { filePath };
  }

  const outputPath = path.join(
    os.tmpdir(),
    `runninghub_upload_${Date.now()}_${Math.random().toString(36).slice(2)}.wav`
  );

  await runProcess(ffmpegPath, [
    '-y',
    '-i', filePath,
    '-vn',
    '-ac', '1',
    '-ar', '24000',
    '-c:a', 'pcm_s16le',
    outputPath,
  ]);

  const stats = fs.statSync(outputPath);
  if (stats.size <= 44) {
    fs.rmSync(outputPath, { force: true });
    throw new Error('normalized audio file is empty');
  }

  logger.info('[RunningHub] Audio normalized before upload', {
    source: filePath,
    outputPath,
    outputSize: stats.size,
  });

  return { filePath: outputPath, cleanupPath: outputPath };
}

/**
 * 任务状态
 */
export type TaskStatus = 'QUEUED' | 'RUNNING' | 'SUCCESS' | 'FAILED';

/**
 * 任务查询结果
 */
export interface TaskQueryResult {
  taskId: string;
  status: TaskStatus;
  errorCode?: string;
  errorMessage?: string;
  results?: Array<{
    url: string;
    outputType: string;
  }>;
}

/**
 * RunningHub API 客户端
 */
export class RunningHubClient {
  private config: Required<RunningHubConfig>;
  private axios: AxiosInstance;

  constructor(config?: Partial<RunningHubConfig>) {
    // 默认配置（云端模式1）
    this.config = {
      apiKey: config?.apiKey || getElectronSystemConfigSync().runninghub_api_key,
      workflowId: config?.workflowId || '2013514129943826433',
      baseUrl: config?.baseUrl || 'https://www.runninghub.cn',
      apiVersion: config?.apiVersion || 'v2',
      instanceType: config?.instanceType || 'default',
      nodes: config?.nodes || {
        videoNode: '6',
        audioNode: '5'
      }
    };

    // 创建axios实例
    this.axios = axios.create({
      baseURL: this.config.baseUrl,
      timeout: 600000, // 10分钟超时
      headers: {
        'Host': 'www.runninghub.cn'
      }
    });

    logger.info('[RunningHub] 客户端已初始化', {
      baseUrl: this.config.baseUrl,
      workflowId: this.config.workflowId
    });
  }

  /**
   * 上传文件到RunningHub
   * @param filePath 本地文件路径
   * @param fileType 文件类型 (video | audio)
   * @returns 返回文件在服务器上的fileName
   */
  async uploadFile(filePath: string, fileType: 'video' | 'audio'): Promise<string> {
    logger.info(`[RunningHub] 开始上传${fileType}文件`, { filePath });

    try {
      if (!fs.existsSync(filePath)) {
        throw new Error(`文件不存在: ${filePath}`);
      }

      const stats = fs.statSync(filePath);
      const fileSizeMB = stats.size / (1024 * 1024);
      if (fileSizeMB > 100) {
        throw new Error(`文件大小超过100MB限制: ${fileSizeMB.toFixed(2)}MB`);
      }

      const FormDataClass = (global as any).FormData;
      if (!FormDataClass) {
        throw new Error('当前环境不支持全局 FormData');
      }

      let uploadPath = filePath;
      let cleanupPath: string | undefined;
      if (fileType === 'audio') {
        const normalized = await normalizeAudioForRunningHubUpload(filePath);
        uploadPath = normalized.filePath;
        cleanupPath = normalized.cleanupPath;
      }

      let fileName: string | null = null;

      try {
        try {
          const formData = new FormDataClass();
          const fileBuffer = fs.readFileSync(uploadPath);
          const blob = new Blob([fileBuffer]);
          formData.append('file', blob, path.basename(uploadPath));

          const response = await this.axios.post('/openapi/v2/media/upload/binary', formData, {
            headers: {
              'Authorization': `Bearer ${this.config.apiKey}`
            },
            maxContentLength: Infinity,
            maxBodyLength: Infinity
          });

          if (response.data.code === 0 && response.data.data?.fileName) {
            fileName = response.data.data.fileName;
            logger.info(`[RunningHub] v2接口上传${fileType}成功`, { fileName });
          } else {
            logger.warn(`[RunningHub] v2接口返回异常，尝试v1`, { code: response.data.code, msg: response.data.message || response.data.msg });
          }
        } catch (v2Err: any) {
          logger.warn(`[RunningHub] v2接口上传失败，回退到v1`, { error: v2Err.message });
        }

        if (!fileName) {
          const formData = new FormDataClass();
          const fileBuffer = fs.readFileSync(uploadPath);
          const blob = new Blob([fileBuffer]);
          formData.append('file', blob, path.basename(uploadPath));
          formData.append('apiKey', this.config.apiKey);

          const response = await this.axios.post('/task/openapi/upload', formData, {
            maxContentLength: Infinity,
            maxBodyLength: Infinity
          });

          if (response.data.code !== 200 && response.data.code !== 0) {
            throw new Error(response.data.msg || response.data.message || 'v1上传失败');
          }

          fileName = response.data.data?.fileName || response.data.data;
          if (typeof fileName !== 'string') {
            throw new Error('v1接口未返回有效fileName');
          }
          logger.info(`[RunningHub] v1接口上传${fileType}成功`, { fileName });
        }

        return fileName;
      } finally {
        if (cleanupPath) {
          fs.rmSync(cleanupPath, { force: true });
        }
      }
    } catch (error: any) {
      const detail = error.response?.data || error.response?.statusText || '';
      logger.error(`[RunningHub] ${fileType}文件上传失败`, {
        filePath,
        fileSize: fs.existsSync(filePath) ? (fs.statSync(filePath).size / (1024 * 1024)).toFixed(2) + 'MB' : 'unknown',
        httpStatus: error.response?.status,
        responseData: typeof detail === 'string' ? detail.substring(0, 500) : JSON.stringify(detail).substring(0, 500),
        error: error.message
      });
      throw new Error(`上传${fileType}文件失败: ${error.message}${error.response?.status ? ` (HTTP ${error.response?.status})` : ''}`);
    }
  }

  /**
   * 创建云端合成任务
   * @param videoFileName 视频文件名
   * @param audioFileName 音频文件名
   * @param audioDuration 音频时长（秒）
   * @returns 返回任务ID
   */
  async createTask(videoFileName: string, audioFileName: string, audioDuration?: number): Promise<string> {
    logger.info('[RunningHub] 开始创建任务', { videoFileName, audioFileName, audioDuration, workflowId: this.config.workflowId, apiVersion: this.config.apiVersion });

    try {
      if (this.config.apiVersion === 'v2') {
        const nodeInfoList = [
          {
            nodeId: this.config.nodes.videoNode,
            fieldName: 'video',
            fieldValue: videoFileName,
            description: '请导入视频'
          },
          {
            nodeId: this.config.nodes.audioNode,
            fieldName: 'audio',
            fieldValue: audioFileName,
            description: '请导入新的音频'
          }
        ];

        if (audioDuration && this.config.nodes.durationNode) {
          nodeInfoList.push({
            nodeId: this.config.nodes.durationNode,
            fieldName: 'value',
            fieldValue: String(Math.ceil(audioDuration)),
            description: '视频时长设置（秒）'
          });
        }

        if (this.config.nodes.fpsNode) {
          nodeInfoList.push({
            nodeId: this.config.nodes.fpsNode,
            fieldName: 'int',
            fieldValue: '30',
            description: '视频帧率'
          });
        }
        if (this.config.nodes.widthNode) {
          nodeInfoList.push({
            nodeId: this.config.nodes.widthNode,
            fieldName: 'int',
            fieldValue: '576',
            description: '视频宽度'
          });
        }
        if (this.config.nodes.heightNode) {
          nodeInfoList.push({
            nodeId: this.config.nodes.heightNode,
            fieldName: 'int',
            fieldValue: '1024',
            description: '视频高度'
          });
        }

        const payload = {
          randomSeed: true,
          nodeInfoList,
          instanceType: this.config.instanceType || 'default',
          retainSeconds: 0,
          usePersonalQueue: false
        };

        logger.info('[RunningHub] v2 API请求payload', payload);

        const response = await this.axios.post(`/openapi/v2/run/ai-app/${this.config.workflowId}`, payload, {
          headers: {
            'Content-Type': 'application/json',
            'Authorization': `Bearer ${this.config.apiKey}`
          }
        });

        logger.info('[RunningHub] v2 API响应', {
          status: response.status,
          data: response.data
        });

        const taskId = response.data.taskId;

        if (!taskId) {
          throw new Error(response.data.errorMessage || '创建任务失败：未返回taskId');
        }

        logger.info('[RunningHub] 任务创建成功（v2）', { taskId, status: response.data.status });

        return taskId;
      }

      const payload = {
        apiKey: this.config.apiKey,
        workflowId: this.config.workflowId,
        nodeInfoList: [
          {
            nodeId: this.config.nodes.videoNode,
            fieldName: 'file',
            fieldValue: videoFileName
          },
          {
            nodeId: this.config.nodes.audioNode,
            fieldName: 'audio',
            fieldValue: audioFileName
          }
        ]
      };

      logger.info('[RunningHub] v1 API请求payload', payload);

      const response = await this.axios.post('/task/openapi/create', payload, {
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.config.apiKey}`
        }
      });

      logger.info('[RunningHub] v1 API响应', {
        status: response.status,
        data: response.data
      });

      if (response.data.code !== 0) {
        throw new Error(response.data.msg || '创建任务失败');
      }

      const taskId = response.data.data.taskId;
      logger.info('[RunningHub] 任务创建成功（v1）', {
        taskId,
        status: response.data.data.taskStatus
      });

      return taskId;
    } catch (error: any) {
      logger.error('[RunningHub] 创建任务失败', {
        error: error.message,
        response: error.response?.data,
        status: error.response?.status,
        statusText: error.response?.statusText,
        workflowId: this.config.workflowId,
        apiVersion: this.config.apiVersion
      });

      let errorMsg = error.message;
      if (error.response?.data) {
        errorMsg = `${error.message} (${JSON.stringify(error.response.data)})`;
      }
      throw new Error(`创建任务失败: ${errorMsg}`);
    }
  }

  /**
   * 查询任务状态
   * @param taskId 任务ID
   * @returns 返回任务查询结果
   */
  async queryTask(taskId: string): Promise<TaskQueryResult> {
    try {
      const response = await this.axios.post('/openapi/v2/query', {
        taskId
      }, {
        headers: {
          'Content-Type': 'application/json',
          'Authorization': `Bearer ${this.config.apiKey}`
        }
      });

      const result: TaskQueryResult = {
        taskId: response.data.taskId,
        status: response.data.status,
        errorCode: response.data.errorCode,
        errorMessage: response.data.errorMessage,
        results: response.data.results
      };

      return result;
    } catch (error: any) {
      logger.error('[RunningHub] 查询任务失败', {
        taskId,
        error: error.message
      });
      throw new Error(`查询任务失败: ${error.message}`);
    }
  }

  /**
   * 轮询查询任务状态直到完成
   * @param taskId 任务ID
   * @param maxRetries 最大重试次数，默认360次（6分钟）
   * @param interval 查询间隔，默认1000ms
   * @param onProgress 进度回调函数
   * @returns 返回最终结果
   */
  async waitForTaskComplete(
    taskId: string,
    maxRetries: number = 360,
    interval: number = 1000,
    onProgress?: (status: TaskStatus, retryCount: number) => void
  ): Promise<TaskQueryResult> {
    logger.info('[RunningHub] 开始轮询任务状态', { taskId, maxRetries });

    let retryCount = 0;

    while (retryCount < maxRetries) {
      const result = await this.queryTask(taskId);

      // 调用进度回调
      if (onProgress) {
        onProgress(result.status, retryCount);
      }

      // 任务完成
      if (result.status === 'SUCCESS') {
        logger.info('[RunningHub] 任务完成', {
          taskId,
          retryCount,
          results: result.results
        });
        return result;
      }

      // 任务失败
      if (result.status === 'FAILED') {
        logger.error('[RunningHub] 任务失败', {
          taskId,
          errorCode: result.errorCode,
          errorMessage: result.errorMessage
        });
        throw new Error(result.errorMessage || '任务执行失败');
      }

      // 继续等待
      retryCount++;
      await new Promise(resolve => setTimeout(resolve, interval));
    }

    // 超时
    throw new Error(`任务超时: 已等待 ${maxRetries * interval / 1000} 秒`);
  }
}
