/**
 * Python 执行器插件
 * 使用 child_process 执行 Python 代码和脚本
 */

const { Plugin } = require('../src/core/plugin-base')
const { logger } = require('../src/utils/logger')
const log = logger.child('PythonExecutor')
const { spawn } = require('child_process')
const fs = require('fs')
const path = require('path')
const os = require('os')
const { z } = require('zod')

class PythonExecutorPlugin extends Plugin {
  constructor(config = {}) {
    super()
    this.name = 'python-executor'
    this.version = '1.0.0'
    this.description = 'Python 执行器，用于运行 Python 代码和脚本，禁止传入无用的emoji'
    this.priority = 15

    this.config = {
      pythonPath: config.pythonPath || 'python',  // python 或 python3
      timeout: config.timeout || 120000,         // 2分钟超时
      baseDir: config.baseDir || '.agent/python-scripts',
      pipPath: config.pipPath || null             // 可选，自定义 pip 路径
    }

    this.system = true 

    this._framework = null
    this._tempDir = null
  }

  install(framework) {
    this._framework = framework

    // 确保临时目录存在
    this._tempDir = path.join(os.tmpdir(), `vb-python-${process.pid}`)
    if (!fs.existsSync(this._tempDir)) {
      fs.mkdirSync(this._tempDir, { recursive: true })
    }

    return this
  }

  start(framework) {
    // 注册 python-execute 工具
    framework.registerTool({
      name: 'python-execute',
      description: '执行 Python 代码，禁止传入无用的emoji',
      inputSchema: z.object({
        code: z.string().describe('Python 代码'),
        timeout: z.number().optional().describe('超时时间（毫秒），默认 120000'),
        input_data: z.record(z.any()).optional().describe('输入数据对象，可在代码中通过 input_data.get("key") 访问')
      }),
      execute: async (args) => {
        return this._executePython(args.code, args.timeout || this.config.timeout, args.input_data)
      }
    })

    // 注册 python_script 工具
    framework.registerTool({
      name: 'python_script',
      description: '执行 Python 脚本文件',
      inputSchema: z.object({
        scriptPath: z.string().describe('Python 脚本文件路径'),
        args: z.string().optional().describe('传递给脚本的命令行参数'),
        timeout: z.number().optional().describe('超时时间（毫秒）')
      }),
      execute: async (args) => {
        return this._executeScript(args.scriptPath, args.args, args.timeout || this.config.timeout)
      }
    })

    // 注册 pip_install 工具
    framework.registerTool({
      name: 'pip_install',
      description: '安装 Python 包',
      inputSchema: z.object({
        package: z.string().describe('包名 (例如: numpy, pandas>=1.0)'),
        upgrade: z.boolean().optional().describe('是否升级已存在的包'),
        timeout: z.number().optional().describe('超时时间（毫秒）')
      }),
      execute: async (args) => {
        return this._pipInstall(args.package, args.upgrade, args.timeout || this.config.timeout)
      }
    })

    return this
  }

  /**
   * 执行 Python 代码
   * @private
   */
  _executePython(code, timeout, inputData) {
    return new Promise((resolve) => {
      const startTime = Date.now()
      let output = ''
      let errorOutput = ''

      // 创建临时脚本文件
      const tempFile = path.join(this._tempDir, `temp_${Date.now()}.py`)

      // 注入 input_data 变量
      let inputDataSetup = ''
      if (inputData) {
        const inputDataJson = JSON.stringify(inputData).replace(/'/g, "\\'")
        inputDataSetup = `import json\ninput_data = json.loads('${inputDataJson}')\n`
      } else {
        inputDataSetup = `input_data = {}\n`
      }

      // 包装代码，添加错误处理
      const wrappedCode = `
import sys
import traceback
${inputDataSetup}
try:
${code.split('\n').map(line => '    ' + line).join('\n')}
except SystemExit:
    pass
except Exception:
    traceback.print_exc()
    sys.exit(1)
`

      fs.writeFileSync(tempFile, wrappedCode, 'utf-8')

      const proc = spawn(this.config.pythonPath, [tempFile], {
        timeout,
        env: { ...process.env }
      })

      proc.stdout.on('data', (data) => {
        output += data.toString()
      })

      proc.stderr.on('data', (data) => {
        errorOutput += data.toString()
      })

      proc.on('close', (code) => {
        // 清理临时文件
        try {
          fs.unlinkSync(tempFile)
        } catch (e) { }

        const elapsed = Date.now() - startTime

        // 限制返回数据大小，避免上下文超限（限制为 5000 字符）
        const MAX_OUTPUT_LENGTH = 5000
        let truncatedOutput = output
        let truncated = false
        if (output.length > MAX_OUTPUT_LENGTH) {
          truncatedOutput = output.substring(0, MAX_OUTPUT_LENGTH) + `\n... [输出已截断，总长度 ${output.length} 字符]`
          truncated = true
        }

        resolve({
          success: code === 0,
          exitCode: code,
          stdout: truncatedOutput,
          stderr: errorOutput.length > MAX_OUTPUT_LENGTH
            ? errorOutput.substring(0, MAX_OUTPUT_LENGTH) + `\n... [错误输出已截断]`
            : errorOutput,
          elapsed,
          timedOut: code === null,
          truncated
        })
      })

      proc.on('error', (err) => {
        // 清理临时文件
        try {
          fs.unlinkSync(tempFile)
        } catch (e) { }

        resolve({
          success: false,
          error: err.message
        })
      })

      setTimeout(() => {
        if (!proc.killed) {
          proc.kill('SIGTERM')
          try {
            fs.unlinkSync(tempFile)
          } catch (e) { }

          // 限制返回数据大小
          const MAX_OUTPUT_LENGTH = 5000
          const truncatedOutput = output.length > MAX_OUTPUT_LENGTH
            ? output.substring(0, MAX_OUTPUT_LENGTH) + `\n... [输出已截断，总长度 ${output.length} 字符]`
            : output

          resolve({
            success: false,
            error: `Python execution timed out after ${timeout}ms`,
            stdout: truncatedOutput,
            stderr: errorOutput.length > MAX_OUTPUT_LENGTH
              ? errorOutput.substring(0, MAX_OUTPUT_LENGTH) + `\n... [错误输出已截断]`
              : errorOutput,
            timedOut: true
          })
        }
      }, timeout)
    })
  }

  /**
   * 执行 Python 脚本文件
   * @private
   */
  _executeScript(scriptPath, args, timeout) {
    return new Promise((resolve) => {
      const startTime = Date.now()
      let output = ''
      let errorOutput = ''

      // 检查文件是否存在
      if (!fs.existsSync(scriptPath)) {
        resolve({
          success: false,
          error: `Script file not found: ${scriptPath}`
        })
        return
      }

      const procArgs = [scriptPath]
      if (args) {
        procArgs.push(...args.split(' '))
      }

      const proc = spawn(this.config.pythonPath, procArgs, {
        timeout,
        env: { ...process.env }
      })

      proc.stdout.on('data', (data) => {
        output += data.toString()
      })

      proc.stderr.on('data', (data) => {
        errorOutput += data.toString()
      })

      proc.on('close', (code) => {
        const elapsed = Date.now() - startTime
        resolve({
          success: code === 0,
          exitCode: code,
          stdout: output,
          stderr: errorOutput,
          elapsed,
          timedOut: code === null
        })
      })

      proc.on('error', (err) => {
        resolve({
          success: false,
          error: err.message
        })
      })

      setTimeout(() => {
        if (!proc.killed) {
          proc.kill('SIGTERM')
          resolve({
            success: false,
            error: `Script execution timed out after ${timeout}ms`,
            stdout: output,
            stderr: errorOutput,
            timedOut: true
          })
        }
      }, timeout)
    })
  }

  /**
   * 安装 Python 包
   * @private
   */
  _pipInstall(packageName, upgrade, timeout) {
    return new Promise((resolve) => {
      const startTime = Date.now()
      let output = ''
      let errorOutput = ''

      const pipPath = this.config.pipPath || this.config.pythonPath.replace('python', 'pip')
      const args = [packageName]
      if (upgrade) args.unshift('-U')
      if (upgrade === undefined && packageName.includes('>=')) args.unshift('-U')

      const proc = spawn(pipPath, ['install', ...args], {
        timeout,
        env: { ...process.env }
      })

      proc.stdout.on('data', (data) => {
        output += data.toString()
      })

      proc.stderr.on('data', (data) => {
        errorOutput += data.toString()
      })

      proc.on('close', (code) => {
        const elapsed = Date.now() - startTime
        resolve({
          success: code === 0,
          exitCode: code,
          stdout: output,
          stderr: errorOutput,
          elapsed,
          package: packageName,
          upgraded: upgrade || packageName.includes('>=')
        })
      })

      proc.on('error', (err) => {
        resolve({
          success: false,
          error: err.message,
          package: packageName
        })
      })

      setTimeout(() => {
        if (!proc.killed) {
          proc.kill('SIGTERM')
          resolve({
            success: false,
            error: `pip install timed out after ${timeout}ms`,
            stdout: output,
            stderr: errorOutput,
            timedOut: true,
            package: packageName
          })
        }
      }, timeout)
    })
  }

  reload(framework) {
    this._framework = framework
  }

  uninstall(framework) {
    // 清理临时目录
    if (this._tempDir && fs.existsSync(this._tempDir)) {
      try {
        fs.rmSync(this._tempDir, { recursive: true, force: true })
      } catch (e) {
        log.warn(` Failed to cleanup temp dir: ${e.message}`)
      }
    }
    this._framework = null
    this._tempDir = null
  }
}

module.exports = PythonExecutorPlugin