#!/usr/bin/env node

/**
 * MJ Image Generator - Midjourney 图片生成工具
 *
 * 功能：
 * - 提交 imagine 任务
 * - 轮询直到完成
 * - 下载 4 合 1 结果图
 * - 自动拆分为 4 张独立图片
 * - 支持参考图（图生图）
 *
 * 用法：
 *   node mj-image-generator.js "a cute cat" -o ./output
 *   node mj-image-generator.js "cyberpunk city" -o ./output -r http://example.com/ref.jpg
 *   node mj-image-generator.js batch ./prompts.txt -o ./output
 */

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

// ============================================================================
// 配置
// ============================================================================

function loadConfig() {
  const configPath = path.join(__dirname, '..', '..', 'config.json')
  if (fs.existsSync(configPath)) {
    return JSON.parse(fs.readFileSync(configPath, 'utf-8'))
  }
  return {}
}

const cfg = loadConfig()

const Config = {
  baseUrl: cfg.mjApiBaseUrl,
  apiKey: cfg.mjApiKey || '',
  pollInterval: 5000,     // 轮询间隔 5 秒
  maxPollTime: 300000,    // 最大等待 5 分钟
}

// ============================================================================
// API 调用
// ============================================================================

const MJApi = {
  /**
   * 提交 imagine 任务
   */
  async submit(prompt, options = {}) {
    const { referenceImages = [], botType = 'mj', aspectRatio = '', mjParams = '', styleWeight = 100 } = options

    let finalPrompt = prompt
    if (referenceImages.length > 0) {
      const srefSection = `--sref ${referenceImages.join(' ')} --sw ${styleWeight}`
      finalPrompt = `${prompt} ${srefSection}`.trim()
    }
    if (aspectRatio) {
      finalPrompt = `${finalPrompt} --ar ${aspectRatio}`
    }
    if (mjParams) {
      finalPrompt = `${finalPrompt} ${mjParams}`
    }

    const body = {
      prompt: finalPrompt,
      base64Array: [],
      botType,
    }

    console.log(`\n📡 提交 MJ 任务`)
    console.log(`   提示词: ${finalPrompt.substring(0, 80)}...`)
    console.log(`   参考图: ${referenceImages.length} 张`)

    const res = await fetch(`${Config.baseUrl}/mj/submit/imagine`, {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': `Bearer ${Config.apiKey}`,
      },
      body: JSON.stringify(body),
    })

    const result = await res.json()

    if (result.code !== 1 && result.code !== '1') {
      throw new Error(`MJ 提交失败: code=${result.code}, ${result.description || JSON.stringify(result)}`)
    }

    const taskId = result.result
    console.log(`   任务 ID: ${taskId}`)
    return taskId
  },

  /**
   * 轮询任务状态
   */
  async poll(taskId) {
    const startTime = Date.now()
    let lastProgress = ''

    console.log(`\n⏳ 等待 MJ 生成...`)

    while (Date.now() - startTime < Config.maxPollTime) {
      const res = await fetch(`${Config.baseUrl}/mj/task/${taskId}/fetch`, {
        headers: { 'Authorization': `Bearer ${Config.apiKey}` },
      })

      const task = await res.json()
      const status = task.status

      if (status === 'SUCCESS') {
        console.log(`\n✅ 生成完成!`)
        console.log(`   图片 URL: ${task.imageUrl}`)
        return {
          success: true,
          imageUrl: task.imageUrl,
          prompt: task.prompt || task.promptEn,
        }
      }

      if (status === 'FAILURE') {
        const errMsg = task.failReason || '未知原因'
        throw new Error(`MJ 生成失败: ${errMsg}`)
      }

      // 显示进度
      const progress = task.progress || ''
      if (progress !== lastProgress) {
        lastProgress = progress
        process.stdout.write(`   进度: ${progress}%   状态: ${status}\r`)
      }

      await new Promise(r => setTimeout(r, Config.pollInterval))
    }

    throw new Error(`MJ 生成超时 (${Config.maxPollTime / 1000}s)`)
  },
}

// ============================================================================
// 图片处理
// ============================================================================

const ImageUtils = {
  /**
   * 下载图片到本地
   */
  async download(url, outputPath) {
    const protocol = url.startsWith('https') ? https : http

    return new Promise((resolve, reject) => {
      const file = fs.createWriteStream(outputPath)
      protocol.get(url, (response) => {
        // 处理重定向
        if (response.statusCode >= 300 && response.statusCode < 400 && response.headers.location) {
          file.close()
          fs.unlinkSync(outputPath)
          return ImageUtils.download(response.headers.location, outputPath).then(resolve).catch(reject)
        }
        response.pipe(file)
        file.on('finish', () => {
          file.close()
          resolve(outputPath)
        })
      }).on('error', (err) => {
        file.close()
        if (fs.existsSync(outputPath)) fs.unlinkSync(outputPath)
        reject(err)
      })
    })
  },

  /**
   * 将 4 合 1 图片拆分为 4 张独立图片
   */
  async split4(gridImagePath, outputDir, prefix = 'mj') {
    const image = sharp(gridImagePath)
    const metadata = await image.metadata()
    const { width, height } = metadata

    // MJ 4 合 1 是 2x2 网格，每格约一半
    const halfW = Math.floor(width / 2)
    const halfH = Math.floor(height / 2)

    const positions = [
      { name: `${prefix}_1`, x: 0, y: 0 },           // 左上
      { name: `${prefix}_2`, x: halfW, y: 0 },        // 右上
      { name: `${prefix}_3`, x: 0, y: halfH },        // 左下
      { name: `${prefix}_4`, x: halfW, y: halfH },    // 右下
    ]

    const files = []
    for (const pos of positions) {
      const outputPath = path.join(outputDir, `${pos.name}.png`)
      await sharp(gridImagePath)
        .extract({ left: pos.x, top: pos.y, width: halfW, height: halfH })
        .toFile(outputPath)
      files.push(outputPath)
    }

    return files
  },
}

// ============================================================================
// 核心流程
// ============================================================================

async function generate(prompt, options = {}) {
  const {
    outputDir = './output',
    referenceImages = [],
    aspectRatio = '',
    mjParams = '',
    split = true,
    keepGrid = false,
  } = options

  if (!Config.apiKey) {
    throw new Error('未配置 mjApiKey，请在 config.json 中添加')
  }

  fs.mkdirSync(outputDir, { recursive: true })

  // 1. 提交任务
  const taskId = await MJApi.submit(prompt, { referenceImages, aspectRatio, mjParams, styleWeight: options.styleWeight })

  // 2. 轮询等待
  const result = await MJApi.poll(taskId)

  // 3. 下载
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
  const gridFile = path.join(outputDir, `${timestamp}_grid.png`)
  await ImageUtils.download(result.imageUrl, gridFile)
  console.log(`   下载完成: ${gridFile}`)

  // 4. 拆分
  const allFiles = [gridFile]
  if (split) {
    const prefix = timestamp
    const splitFiles = await ImageUtils.split4(gridFile, outputDir, prefix)
    allFiles.push(...splitFiles)
    console.log(`   拆分完成: ${splitFiles.length} 张图片`)

    if (!keepGrid) {
      fs.unlinkSync(gridFile)
      allFiles.shift()
      console.log(`   已删除网格图`)
    }
  }

  return {
    success: true,
    taskId,
    imageUrl: result.imageUrl,
    files: allFiles,
  }
}

/**
 * 批量生成（并发提交 + 并行轮询）
 */
async function batchGenerate(prompts, options = {}) {
  const concurrency = options.concurrency || prompts.length
  const results = new Array(prompts.length).fill(null)

  // 分批提交，每批 concurrency 个并行
  for (let batchStart = 0; batchStart < prompts.length; batchStart += concurrency) {
    const batchEnd = Math.min(batchStart + concurrency, prompts.length)
    const batchIndices = []
    for (let i = batchStart; i < batchEnd; i++) batchIndices.push(i)

    // 并行提交所有任务
    console.log(`\n📡 批量提交 [${batchStart + 1}-${batchEnd}/${prompts.length}]...`)
    const taskIds = await Promise.all(batchIndices.map(async (i) => {
      try {
        const taskId = await MJApi.submit(prompts[i], {
          referenceImages: options.referenceImages,
          aspectRatio: options.aspectRatio,
          mjParams: options.mjParams,
          styleWeight: options.styleWeight,
        })
        return { i, taskId }
      } catch (err) {
        console.error(`   [${i + 1}] ❌ 提交失败: ${err.message}`)
        results[i] = { success: false, prompt: prompts[i], error: err.message }
        return { i, taskId: null }
      }
    }))

    // 过滤出成功的任务，并行轮询
    const activeTasks = taskIds.filter(t => t.taskId)
    console.log(`   已提交 ${activeTasks.length} 个任务，并行等待生成...\n`)

    const pollResults = await Promise.all(activeTasks.map(async ({ i, taskId }) => {
      try {
        const result = await MJApi.poll(taskId)
        // 下载 + 拆分
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
        const gridFile = path.join(options.outputDir || './output', `${timestamp}_grid.png`)
        fs.mkdirSync(path.dirname(gridFile), { recursive: true })
        await ImageUtils.download(result.imageUrl, gridFile)

        const allFiles = [gridFile]
        if (options.split !== false) {
          const prefix = timestamp
          const splitFiles = await ImageUtils.split4(gridFile, options.outputDir || './output', prefix)
          allFiles.push(...splitFiles)
          if (!options.keepGrid) {
            fs.unlinkSync(gridFile)
            allFiles.shift()
          }
        }

        console.log(`   [${i + 1}/${prompts.length}] ✅ 完成`)
        return { i, success: true, prompt: prompts[i], taskId, imageUrl: result.imageUrl, files: allFiles }
      } catch (err) {
        console.error(`   [${i + 1}/${prompts.length}] ❌ 失败: ${err.message}`)
        return { i, success: false, prompt: prompts[i], error: err.message }
      }
    }))

    for (const r of pollResults) results[r.i] = r
  }

  const ok = results.filter(r => r && r.success).length
  console.log(`\n✨ 批量完成: ${ok}/${prompts.length} 成功`)

  return results
}

// ============================================================================
// CLI
// ============================================================================

function showHelp() {
  console.log(`
🎨 MJ Image Generator - Midjourney 图片生成工具

用法:
  node mj-image-generator.js <prompt> [options]
  node mj-image-generator.js batch <file> [options]

选项:
  -o, --output <dir>       输出目录 (默认: ./output)
  -r, --ref <urls>         参考图 URL，逗号分隔
  -a, --ar <ratio>         宽高比 (1:1, 16:9, 9:16, 3:4, 4:3 等)
  -c, --concurrency <n>    并发数 (默认: 全部并行)
  --no-split               不拆分 4 合 1
  --keep-grid              保留原始网格图
  -h, --help               帮助

示例:
  node mj-image-generator.js "a cute cat" -o ./cats
  node mj-image-generator.js "cyberpunk city" -a 16:9
  node mj-image-generator.js "portrait" -r http://img.com/ref.jpg -a 9:16
  node mj-image-generator.js batch ./prompts.txt -o ./batch
`)
}

async function main() {
  const args = process.argv.slice(2)

  if (args.includes('-h') || args.includes('--help') || args.length === 0) {
    showHelp()
    return
  }

  let command = 'generate'
  let params = []
  const options = { outputDir: './output', split: true, keepGrid: false, referenceImages: [], aspectRatio: '', concurrency: 0, mjParams: '', styleWeight: 100 }

  let i = 0
  if (args[0] === 'batch') {
    command = 'batch'
    i = 1
  }

  while (i < args.length) {
    const arg = args[i]
    if (arg === '-o' || arg === '--output') {
      options.outputDir = args[++i]
    } else if (arg === '-a' || arg === '--ar') {
      options.aspectRatio = args[++i]
    } else if (arg === '-r' || arg === '--ref') {
      options.referenceImages = args[++i].split(',').map(s => s.trim()).filter(Boolean)
    } else if (arg === '--no-split') {
      options.split = false
    } else if (arg === '--keep-grid') {
      options.keepGrid = true
    } else if (arg === '-c' || arg === '--concurrency') {
      options.concurrency = parseInt(args[++i], 10) || 0
    } else if (arg === '--mj-params') {
      options.mjParams = args[++i]
    } else if (arg === '--sw') {
      options.styleWeight = parseInt(args[++i], 10) || 100
    } else {
      params.push(arg)
    }
    i++
  }

  if (command === 'batch') {
    const filePath = params[0]
    if (!filePath || !fs.existsSync(filePath)) {
      console.error('请提供提示词文件路径')
      process.exit(1)
    }
    const prompts = fs.readFileSync(filePath, 'utf-8')
      .split('\n').filter(l => l.trim()).map(l => l.trim())
    await batchGenerate(prompts, options)
  } else {
    const prompt = params.join(' ')
    if (!prompt) {
      console.error('请提供提示词')
      process.exit(1)
    }
    await generate(prompt, options)
  }
}

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

module.exports = { generate, batchGenerate, MJApi, ImageUtils }

if (require.main === module) {
  main().catch(err => {
    console.error(`\n❌ 错误: ${err.message}`)
    process.exit(1)
  })
}
