import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import { resolve } from 'path'
import fs from 'fs'
import { spawn } from 'child_process'

function apiSavePlugin() {
  return {
    name: 'api-save',
    configureServer(server: any) {
      const opencodeBin = resolve(__dirname, 'node_modules', 'opencode-ai', 'bin', 'opencode.exe')

      server.middlewares.use('/api/save', (req: any, res: any) => {
        if (req.method !== 'POST') { res.writeHead(405); res.end(); return }
        let body = ''
        req.on('data', (c: string) => body += c)
        req.on('end', () => {
          try {
            const { path, data } = JSON.parse(body)
            if (!path || typeof path !== 'string' || !path.startsWith('/scenes/')) {
              res.writeHead(400)
              res.end(JSON.stringify({ error: 'invalid path' }))
              return
            }
            const safePath = resolve(__dirname, 'public', '.' + path)
            fs.writeFileSync(safePath, JSON.stringify(data, null, 2))
            res.writeHead(200, { 'Content-Type': 'application/json' })
            res.end(JSON.stringify({ ok: true }))
          } catch (e: any) {
            res.writeHead(400)
            res.end(JSON.stringify({ error: e.message }))
          }
        })
      })

      const dedupMap = new Map<string, number>()

      server.middlewares.use('/api/ai/sessions', (req: any, res: any) => {
        if (req.method !== 'GET') { res.writeHead(405); res.end(); return }
        try {
          const child = spawn(opencodeBin, ['session', 'list', '--format', 'json'], { timeout: 5000, stdio: ['ignore', 'pipe', 'pipe'] })
          let stdout = ''
          let responded = false
          child.stdout.on('data', (d: Buffer) => stdout += d.toString())
          child.on('error', () => {
            if (responded) return
            responded = true
            res.writeHead(200, { 'Content-Type': 'application/json' })
            res.end('[]')
          })
          child.on('close', () => {
            if (responded) return
            responded = true
            res.writeHead(200, { 'Content-Type': 'application/json' })
            res.end(stdout || '[]')
          })
        } catch {
          res.writeHead(200, { 'Content-Type': 'application/json' })
          res.end('[]')
        }
      })

      server.middlewares.use('/api/ai', (req: any, res: any) => {
        if (req.method !== 'POST') { res.writeHead(405); res.end(); return }
        let body = ''
        req.on('data', (c: string) => body += c)
        req.on('end', () => {
          try {
            const { sessionId, userMessage, apiKey, mode } = JSON.parse(body)
            if (!userMessage) { res.writeHead(400); res.end(JSON.stringify({ error: 'missing fields' })); return }

            const dedupKey = `${mode}_${sessionId || ''}_${userMessage}`
            const last = dedupMap.get(dedupKey) || 0
            if (Date.now() - last < 3000) {
              res.writeHead(429)
              res.end(JSON.stringify({ error: 'duplicate request' }))
              return
            }
            dedupMap.set(dedupKey, Date.now())

            const modePrefix = mode === 'code'
              ? `当前项目根目录: ${__dirname}\n代码模式：直接修改 src/ 下的源码文件并保存。需求：`
              : `当前项目根目录: ${__dirname}\nJSON模式：直接修改 JSON 配置文件并保存，用自然语言回复修改了什么。需求：`
            const fullMessage = modePrefix + userMessage
            console.log(`\n[AI] ${mode === 'json' ? 'JSON' : '代码'}模式 | session=${sessionId || '新会话'} | prompt: ${userMessage.substring(0, 120)}${userMessage.length > 120 ? '...' : ''}`)

            const args = ['run', '--model', 'deepseek/deepseek-v4-pro', '--format', 'json']
            if (sessionId) args.push('--session', sessionId)
            args.push(fullMessage)

            const child = spawn(opencodeBin, args, {
              env: { ...process.env, DEEPSEEK_API_KEY: apiKey || process.env.DEEPSEEK_API_KEY || '' },
              timeout: 60000,
              stdio: ['ignore', 'pipe', 'pipe'],
            })

            let stdout = ''
            let stderr = ''
            let responded = false
            child.stdout.on('data', (d: Buffer) => stdout += d.toString())
            child.stderr.on('data', (d: Buffer) => stderr += d.toString())

            child.on('error', (err: any) => {
              if (responded) return
              responded = true
              res.writeHead(503)
              res.end(JSON.stringify({ error: err?.message || 'spawn failed' }))
            })

            child.on('close', async (code) => {
              if (responded) return
              responded = true
              if (code !== 0) {
                console.log(`[AI] 错误: ${code === null ? '超时' : 'exit code ' + code}${stderr ? ' stderr: ' + stderr.substring(0, 200) : ''}`)
                res.writeHead(500)
                res.end(JSON.stringify({ error: code === null ? 'opencode 超时，请重试或简化需求' : 'opencode exited with code ' + code, stderr }))
                return
              }

              let resolvedSessionId = sessionId
              let aiText = ''

              for (const rawLine of stdout.split(/\r?\n/)) {
                const line = rawLine.trim()
                if (!line) continue
                try {
                  const event = JSON.parse(line)
                  if (!resolvedSessionId && event.sessionID) resolvedSessionId = event.sessionID
                  if (event.type === 'step_start') {
                    console.log(`[AI] 思考中...`)
                  } else if (event.type === 'text' && event.part?.text) {
                    aiText += event.part.text
                  } else if (event.type === 'step_finish') {
                    const tokens = event.part?.tokens
                    if (tokens) console.log(`[AI] tokens: in=${tokens.input} out=${tokens.output} cost=$${tokens.cost?.toFixed(6)}`)
                  }
                } catch { continue }
              }

              console.log(`[AI] 响应 | sessions=${resolvedSessionId || ''} | ${aiText.length} chars | ${aiText.substring(0, 300)}${aiText.length > 300 ? '...' : ''}`)

              if (mode === 'json') {
                res.writeHead(200, { 'Content-Type': 'application/json' })
                res.end(JSON.stringify({ result: aiText || 'done', sessionId: resolvedSessionId || '' }))
              } else {
                res.writeHead(200, { 'Content-Type': 'application/json' })
                res.end(JSON.stringify({ result: aiText || 'done', sessionId: resolvedSessionId || '' }))
              }
            })
          } catch (e: any) {
            res.writeHead(400)
            res.end(JSON.stringify({ error: e.message }))
          }
        })
      })
    },
  }
}

export default defineConfig({
  plugins: [vue(), apiSavePlugin()],
  resolve: {
    alias: {
      '@': resolve(__dirname, 'src'),
      '@engine': resolve(__dirname, 'engine'),
    },
  },
  build: {
    rollupOptions: {
      input: {
        main: resolve(__dirname, 'index.html'),
        editor: resolve(__dirname, 'editor/index.html'),
      },
    },
  },
})
