import { useEffect, useRef, useState } from 'react'
import { useParams } from 'react-router-dom'
import JSZip from 'jszip'
import { Zap, Clock } from 'lucide-react'
import { getSkillApi, downloadSkillPackage, getAuthHeaders, API_BASE } from '../services/api.js'
import { SimpleMarkdown, SettingsModal, DownloadButtons } from '../components/EvaluationPanel.jsx'
import { ToolCallLine, ToolResultLine, FilesDownloadBox } from '../components/AgentEventLine.jsx'
import { runSkillRunStreamWithReconnect } from '../services/skillRunStream.js'
import { createRuntimeTimelineState, reduceRuntimeTimeline } from '../services/runtimeTimeline.js'
import HistoryDrawer from '../components/HistoryDrawer.jsx'
import { useConversationHistory } from '../hooks/useConversationHistory.js'
import PreviewPanel from '../components/PreviewPanel.jsx'
import { useAuth } from '../contexts/AuthContext.jsx'
import RuntimeTimeline from '../components/RuntimeTimeline.jsx'

async function extractMdFromZip(blob) {
  let zip
  try {
    zip = await JSZip.loadAsync(blob)
  } catch {
    const text = await blob.text()
    return text.trim() ? text : null
  }
  const entries = []
  zip.forEach((path, entry) => {
    if (entry.dir) return
    if (path.startsWith('__MACOSX/')) return
    if (/\.(md|markdown)$/i.test(path)) entries.push({ path, entry })
  })
  if (!entries.length) return null
  const score = (p) => {
    const base = p.split('/').pop().toLowerCase()
    if (base === 'skill.md') return 0
    if (base === 'readme.md') return 2
    if (/\.md$/i.test(base)) return 3
    return 4
  }
  entries.sort((a, b) => score(a.path) - score(b.path) || a.path.length - b.path.length)
  return await entries[0].entry.async('string')
}

export default function SkillSandboxPage({ overlayMode = false, overlaySkillId = null }) {
  const { user } = useAuth()
  const params = useParams()
  const id = overlaySkillId || params.id
  const [skill, setSkill] = useState(null)
  const [systemPrompt, setSystemPrompt] = useState('')
  const [loadStatus, setLoadStatus] = useState('loading') // loading | ready | error
  const [loadError, setLoadError] = useState('')

  // Skill 执行引擎配置
  const [engineConfig, setEngineConfig] = useState(null)
  const sessionPromptRef = useRef('')
  const sessionReadyRef = useRef(false)
  const skillRef = useRef(null)
  // Agent 会话 ID（跨轮复用）
  const agentSessionIdRef = useRef(null)

  const [showSettings, setShowSettings] = useState(false)
  const [previewUrl, setPreviewUrl] = useState(null)
  const [previewName, setPreviewName] = useState('')

  const [messages, setMessages] = useState([])
  const [input, setInput] = useState('')
  const [isLoading, setIsLoading] = useState(false)
  const [streamError, setStreamError] = useState('')
  const [runtimeTimeline, setRuntimeTimeline] = useState(createRuntimeTimelineState)
  const lastUserMsgRef = useRef('')
  const scrollRef = useRef(null)
  const loadedSkillIdRef = useRef(null)

  // 历史会话
  const history = useConversationHistory('sandbox', user)
  const prevLoadingRef = useRef(false)

  useEffect(() => {
    const el = scrollRef.current
    if (el) el.scrollTop = el.scrollHeight
  }, [messages, runtimeTimeline])

  // SSE 流结束后保存历史
  useEffect(() => {
    if (prevLoadingRef.current && !isLoading) {
      history.save(messages, { skillId: id, skillName: skill?.name || '沙箱会话', sessionId: agentSessionIdRef.current })
    }
    prevLoadingRef.current = isLoading
  }, [isLoading, messages, id, skill])

  function addSystemMsg(text) {
    setMessages(prev => [...prev, { role: 'system', content: text }])
  }

  const handlePreview = (url, filename) => {
    setPreviewUrl(url)
    setPreviewName(filename || '')
  }

  const handleClosePreview = () => {
    setPreviewUrl(null)
    setPreviewName('')
  }

  // 加载引擎配置 + Skill
  useEffect(() => {
    if (loadedSkillIdRef.current === id) return
    loadedSkillIdRef.current = id
    sessionPromptRef.current = ''
    sessionReadyRef.current = false
    agentSessionIdRef.current = null
    skillRef.current = null
    lastUserMsgRef.current = ''
    setSkill(null)
    setSystemPrompt('')
    setEngineConfig(null)
    setMessages([])
    setInput('')
    setIsLoading(false)
    setStreamError('')
    setLoadError('')
    setLoadStatus('loading')
    setPreviewUrl(null)
    setPreviewName('')
    ;(async () => {
      try {
        const authHeaders = getAuthHeaders()

        // 并行获取引擎配置和 Skill 数据
        const [engineData, s] = await Promise.all([
          fetch(`${API_BASE}/skill-run`, { headers: authHeaders }).then(r => r.json()).catch(() => ({ hasEngine: false })),
          getSkillApi(id),
        ])

        setEngineConfig(engineData)
        setSkill(s)
        skillRef.current = s

        let content = `# ${s?.name || '未命名 Skill'}\n\n${s?.desc || s?.description || '（无描述）'}`
        if (s?.package_key) {
          try {
            const blob = await downloadSkillPackage(s.package_key)
            const md = await extractMdFromZip(blob)
            if (md) content = md
          } catch {}
        }
        setSystemPrompt(content)

        if (!engineData?.hasEngine) {
          setLoadStatus('ready')
          addSystemMsg(`已加载 Skill：${s?.name}`)
          addSystemMsg('⚠ 平台未配置 Skill 执行引擎，请管理员到「智能体配置 / 运行时注册」选择并启用运行时')
          return
        }

        // 本地记录 systemPrompt，不再请求远程 session
        sessionPromptRef.current = content
        sessionReadyRef.current = true

        setLoadStatus('ready')
        addSystemMsg(`已加载 Skill：${s?.name}。正在启动 Skill …`)
        // 打开页面后自动发送一次真实试运行，避免只输出“工具调用示例”。
        const initialMsg = `请真实运行这个 Skill【${s?.name || '未命名'}】一次。若信息不足，请使用合理默认假设生成样例版；如果 Skill 需要工具或文件产物，必须真实调用工具生成结果，禁止只展示工具调用格式。`
        doSend(initialMsg)
      } catch (e) {
        setLoadError(e?.message || String(e))
        setLoadStatus('error')
      }
    })()
  }, [id])

  async function doSend(userText) {
    if (!sessionReadyRef.current) {
      addSystemMsg('⚠ Skill 执行引擎未就绪，无法发送消息')
      return
    }
    if (!sessionPromptRef.current) {
      addSystemMsg('Skill 内容尚未加载完成，请稍候')
      return
    }

    setIsLoading(true)
    setStreamError('')
    setRuntimeTimeline(createRuntimeTimelineState())
    lastUserMsgRef.current = userText

    // 收集历史消息（仅保留真实对话角色）
    const historyForApi = messages
      .filter(m => (m.role === 'user' || m.role === 'assistant') && m.content && !m.isStreaming)
      .map(m => ({ role: m.role, content: m.content }))
    historyForApi.push({ role: 'user', content: userText })

    setMessages(prev => [
      ...prev,
      { role: 'user', content: userText },
      { role: 'assistant', content: '', isStreaming: true },
    ])

    let currentAcc = ''
    let lastRenderTs = 0

    function flushDelta(force = false) {
      const now = Date.now()
      if (!force && now - lastRenderTs < 33) return
      lastRenderTs = now
      const snap = currentAcc
      setMessages(prev => {
        const next = prev.slice()
        for (let i = next.length - 1; i >= 0; i--) {
          if (next[i].role === 'assistant' && next[i].isStreaming) {
            next[i] = { ...next[i], content: snap }
            break
          }
        }
        return next
      })
    }
    function finalizeCurrentBubble() {
      flushDelta(true)
      setMessages(prev => {
        const next = prev.slice()
        for (let i = next.length - 1; i >= 0; i--) {
          if (next[i].role === 'assistant' && next[i].isStreaming) {
            next[i] = { ...next[i], isStreaming: false }
            break
          }
        }
        return next
      })
      currentAcc = ''
    }
    function startNewAssistantBubble() {
      currentAcc = ''
      lastRenderTs = 0
      setMessages(prev => [...prev, { role: 'assistant', content: '', isStreaming: true }])
    }

    try {
      const authHeaders = getAuthHeaders()
      let streamErr = null
      const activeSkill = skillRef.current || skill

      await runSkillRunStreamWithReconnect({
        apiBase: API_BASE,
        authHeaders,
        body: {
          mode: 'run',
          systemPrompt: sessionPromptRef.current,
          messages: historyForApi,
          sessionId: agentSessionIdRef.current || undefined,
          packageKey: !agentSessionIdRef.current && activeSkill?.package_key ? activeSkill.package_key : undefined,
        },
        handlers: {
          onEvent: event => setRuntimeTimeline(previous => reduceRuntimeTimeline(previous, event)),
          onSession: (sid) => {
            if (sid) agentSessionIdRef.current = sid
          },
          onResume: (evt) => {
            if (evt?.sessionId) agentSessionIdRef.current = evt.sessionId
            if (evt?.replayed > 0) setStreamError('连接已恢复，正在继续接收结果')
          },
          onHeartbeat: () => {},
          onReconnect: (info) => {
            setStreamError(`连接波动，正在第 ${info.attempt} 次重连`)
          },
          onDelta: (v) => {
            currentAcc += v
            flushDelta(false)
          },
          onToolCall: (name, args) => {
            finalizeCurrentBubble()
          },
          onToolResult: (name, result) => {
            startNewAssistantBubble()
          },
          onFiles: () => {},
          onError: (msg) => { streamErr = msg },
        },
      })

      if (streamErr) throw new Error(streamErr)
      setStreamError('')
      finalizeCurrentBubble()
    } catch (err) {
      const msg = err?.message || String(err)
      setStreamError(msg)
      setMessages(prev => {
        const next = prev.slice()
        for (let i = next.length - 1; i >= 0; i--) {
          if (next[i].role === 'assistant' && next[i].isStreaming) {
            next[i] = { role: 'assistant', content: `❌ 请求失败：${msg}`, isStreaming: false }
            break
          }
        }
        return next
      })
    } finally {
      setIsLoading(false)
    }
  }

  function handleSend() {
    const t = input.trim()
    if (!t || isLoading || loadStatus !== 'ready') return
    setInput('')
    doSend(t)
  }

  function handleRetry() {
    if (isLoading) return
    const t = lastUserMsgRef.current
    if (!t) return
    setStreamError('')
    doSend(t)
  }

  function handleKeyDown(e) {
    if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend() }
  }

  const canSend = sessionReadyRef.current

  const containerCls = overlayMode
    ? 'flex-1 flex flex-col h-full bg-white overflow-hidden'
    : 'fixed inset-0 bg-white flex flex-col'

  return (
    <div className={containerCls}>
      {/* Header（overlayMode 下由 ChatOverlayShell 提供） */}
      {!overlayMode && (
        <div className="flex items-center gap-2 px-4 py-2.5 border-b border-gray-200 flex-shrink-0">
          <span className="text-brand-600 text-[14px]">▶</span>
          <span className="text-[14px] font-semibold text-gray-900">Skill 沙箱</span>
          {skill && (
            <>
              <span className="text-gray-300">·</span>
              <span className="text-[13px] text-gray-600 truncate min-w-0 flex-1">{skill.name}</span>
            </>
          )}
          {engineConfig?.hasEngine && (
            <span className="px-2 py-0.5 text-[11px] rounded-full bg-blue-50 text-blue-600 border border-blue-100 flex-shrink-0 inline-flex items-center gap-1">
              <Zap size={11} strokeWidth={2.4} />
              {engineConfig?.provider?.name || 'Skill 引擎'}
            </span>
          )}
          <span className="px-2 py-0.5 text-[11px] rounded-full bg-emerald-50 text-emerald-700 border border-emerald-200 flex-shrink-0">
            沙箱模式
          </span>
          <button
            onClick={() => setShowSettings(true)}
            className="w-7 h-7 grid place-items-center rounded-md text-gray-500 hover:bg-gray-100 flex-shrink-0"
            title="设置"
          >⚙</button>
        </div>
      )}

      {/* Skill 执行引擎未配置警告 */}
      {loadStatus === 'ready' && !engineConfig?.hasEngine && (
        <div className="px-4 py-2 bg-amber-50 border-b border-amber-200 text-[12px] text-amber-700">
          ⚠ 平台未配置 Skill 执行引擎，请管理员到「智能体配置 / 运行时注册」选择并启用运行时
        </div>
      )}

      {/* 加载状态 */}
      {loadStatus === 'loading' && (
        <div className="flex-1 flex items-center justify-center text-[13px] text-gray-400">
          加载 Skill 中…
        </div>
      )}
      {loadStatus === 'error' && (
        <div className="flex-1 flex items-center justify-center text-[13px] text-rose-500">
          加载失败：{loadError}
        </div>
      )}

      {/* Messages */}
      {loadStatus === 'ready' && (
        <>
          <div ref={scrollRef} className="flex-1 overflow-y-auto px-4 py-4 space-y-2.5">
            {messages.map((m, idx) => {
              if (m.role === 'system') {
                return <div key={idx} className="text-center text-[11px] text-gray-400 italic py-0.5">{m.content}</div>
              }
              if (m.role === 'tool_call') return <ToolCallLine key={idx} name={m.name} args={m.args} />
              if (m.role === 'tool_result') return <ToolResultLine key={idx} name={m.name} result={m.result} sessionId={m.sessionId} onPreview={handlePreview} />
              if (m.role === 'files') return <FilesDownloadBox key={idx} sessionId={m.sessionId} files={m.files} onPreview={handlePreview} />
              if (m.role === 'user') {
                return (
                  <div key={idx} className="flex justify-end">
                    <div className="bg-brand-600 text-white rounded-xl rounded-tr-sm px-4 py-2.5 max-w-[70%] text-[13px] whitespace-pre-wrap break-words">{m.content}</div>
                  </div>
                )
              }
              // assistant
              if (m.isStreaming && !m.content) return null
              return (
                <div key={idx} className="flex justify-start">
                  <div className="bg-gray-50 border border-gray-200 rounded-xl rounded-tl-sm px-4 py-3 max-w-[85%] min-w-[60px]">
                    {m.content ? <SimpleMarkdown text={m.content} /> : <span className="text-gray-400 text-[12px]">思考中…</span>}
                    {m.isStreaming && <span className="inline-block w-1.5 h-3.5 bg-brand-500 ml-0.5 align-middle animate-pulse" />}
                    {!m.isStreaming && m.content && <DownloadButtons text={m.content} />}
                  </div>
                </div>
              )
            })}
            <RuntimeTimeline timeline={runtimeTimeline} isRunning={isLoading} sessionId={agentSessionIdRef.current || ''} onPreview={handlePreview} />
            {messages.length === 0 && (
              <div className="text-center text-[12px] text-gray-400 py-12">
                <div className="text-[32px] mb-3">▶</div>
                <div>Skill 已就绪，输入内容开始测试</div>
              </div>
            )}
            {streamError && !isLoading && lastUserMsgRef.current && (
              <div className="text-center py-3">
                <p className="text-rose-500 text-[12px] mb-2">流中断：{streamError}</p>
                <button
                  onClick={handleRetry}
                  className="px-3 py-1.5 text-[12px] bg-brand-600 text-white rounded-md hover:bg-brand-700"
                >重试上次请求</button>
              </div>
            )}
          </div>

          <div className="relative border-t border-gray-200 px-4 py-3 flex items-end gap-2 flex-shrink-0">
            <button
              onClick={() => history.setShowHistory(true)}
              className="absolute top-1.5 right-2 w-6 h-6 flex items-center justify-center text-gray-400 hover:text-gray-600 rounded"
              title="历史会话"
            >
              <Clock size={14} />
            </button>
            <textarea
              rows={2}
              value={input}
              onChange={e => setInput(e.target.value)}
              onKeyDown={handleKeyDown}
              placeholder={
                !canSend
                  ? '平台未配置 Skill 执行引擎，无法使用'
                  : isLoading
                    ? '生成中…'
                    : '输入测试内容…（Enter 发送，Shift+Enter 换行）'
              }
              disabled={isLoading || !canSend}
              className="flex-1 px-3 py-2 text-[13px] border border-gray-200 rounded-md focus:outline-none focus:border-brand-400 resize-none disabled:bg-gray-50"
            />
            <button
              onClick={handleSend}
              disabled={isLoading || !input.trim() || !canSend}
              className="px-4 py-2 text-[13px] rounded-md text-white bg-brand-600 hover:bg-brand-700 disabled:opacity-50 disabled:cursor-not-allowed flex-shrink-0"
            >
              发送
            </button>
          </div>
        </>
      )}

      {showSettings && (
        <SettingsModal
          settings={{ systemPrompt: '' }}
          engineConfig={engineConfig}
          onClose={() => setShowSettings(false)}
          onSave={() => setShowSettings(false)}
          showSystemPrompt={false}
        />
      )}

      {/* 历史会话抽屉 */}
      <HistoryDrawer
        open={history.showHistory}
        onClose={() => history.setShowHistory(false)}
        conversations={history.conversations}
        onSelect={(conv) => {
          const loaded = history.load(conv)
          setMessages(loaded.messages || [])
          if (loaded.sessionId) agentSessionIdRef.current = loaded.sessionId
        }}
        onDelete={(conv) => history.remove(conv)}
      />

      <PreviewPanel
        url={previewUrl}
        filename={previewName}
        visible={!!previewUrl}
        onClose={handleClosePreview}
      />
    </div>
  )
}
