import { useRef, useState, useEffect } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
import JSZip from 'jszip'
import NodePickerModal, { getNodePath } from '../components/NodePickerModal.jsx'
import { useCategories } from '../contexts/CategoryContext.jsx'
import { useSkills } from '../contexts/SkillsContext.jsx'
import { useAuth } from '../contexts/AuthContext.jsx'
import { uploadPackage } from '../services/api.js'

const ACCEPT = '.md,.markdown,.txt,.zip'
const ACCEPT_EXT = ['md', 'markdown', 'txt', 'zip']

// 从 zip 里挑主文档（仅用于解析名称/描述预填）：优先 SKILL.md / skill.md / README.md
async function extractMainDocFromZip(file) {
  const zip = await JSZip.loadAsync(file)
  const entries = []
  zip.forEach((path, entry) => {
    if (entry.dir) return
    if (path.startsWith('__MACOSX/')) return
    if (/\.(md|markdown|txt)$/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 === 'skill.markdown') return 1
    if (base === 'readme.md') return 2
    if (/\.md$/i.test(base)) return 3
    if (/\.markdown$/i.test(base)) return 4
    return 5
  }
  entries.sort((a, b) => score(a.path) - score(b.path) || a.path.length - b.path.length)
  const main = entries[0]
  const text = await main.entry.async('string')
  return { text, mainPath: main.path, fileCount: Object.keys(zip.files).filter(p => !zip.files[p].dir).length }
}

// 中日韩统一表意文字范围（用于判定是否为中文）
const HAS_CJK = /[\u4e00-\u9fff]/

// 解析 markdown：支持 frontmatter，预填名称优先取中文（标题 → 正文首句），
// 中文都没有再回退到 frontmatter name / H1 / 文件名。
function parseMd(text, fallbackName) {
  let fmName = '', fmDesc = '', body = text
  const fm = /^---\s*\n([\s\S]*?)\n---\s*\n?([\s\S]*)$/.exec(text)
  if (fm) {
    body = fm[2] || ''
    fm[1].split(/\r?\n/).forEach((line) => {
      const m = /^([\w-]+)\s*:\s*(.*)$/.exec(line.trim())
      if (!m) return
      const k = m[1].toLowerCase()
      const v = m[2].trim().replace(/^["']|["']$/g, '')
      if ((k === 'name' || k === 'title') && v) fmName = v
      if ((k === 'description' || k === 'desc') && v) fmDesc = v
    })
  }

  // 收集所有标题（任意层级），方便挑出中文标题
  const headings = []
  const headingRe = /^#{1,6}\s+(.+?)\s*$/gm
  let hm
  while ((hm = headingRe.exec(body))) headings.push(hm[1].trim())

  // 1) 优先：第一个含中文的标题
  let name = headings.find((h) => HAS_CJK.test(h)) || ''

  // 2) 其次：frontmatter name 含中文
  if (!name && fmName && HAS_CJK.test(fmName)) name = fmName

  // 3) 再次：去掉代码块后，正文首个含中文的行/段，截到首个标点前作为短名称
  if (!name) {
    const stripped = body.replace(/```[\s\S]*?```/g, '')
    const lines = stripped.split(/\r?\n/)
    for (const raw of lines) {
      const line = raw.replace(/^[\s>*\-]+/, '').trim()
      if (!line || line.startsWith('#') || line.startsWith('---') || line.startsWith('|')) continue
      if (!HAS_CJK.test(line)) continue
      const cut = line.split(/[。！？!?，,；;：:、\s]/).find((s) => s && HAS_CJK.test(s))
      if (cut) { name = cut.slice(0, 30); break }
    }
  }

  // 4) 最终兜底：原有英文链路（frontmatter name → H1 → 文件名）
  if (!name) name = fmName || headings[0] || fallbackName || '未命名 Skill'

  // 描述：优先取含中文的 frontmatter / 首个段落
  let desc = ''
  if (fmDesc && HAS_CJK.test(fmDesc)) desc = fmDesc
  if (!desc) {
    const stripped = body.replace(/```[\s\S]*?```/g, '')
    const para = stripped
      .split(/\n{2,}/)
      .map((s) => s.trim())
      .find((s) => s && !s.startsWith('#') && !s.startsWith('---') && !s.startsWith('|') && HAS_CJK.test(s))
    if (para) desc = para.replace(/\s+/g, ' ').slice(0, 120)
  }
  if (!desc) {
    desc = fmDesc || ''
    if (!desc) {
      const p = body
        .split(/\n{2,}/)
        .map((s) => s.trim())
        .find((s) => s && !s.startsWith('#') && !s.startsWith('---'))
      if (p) desc = p.slice(0, 120)
    }
  }

  return { name: (name || '未命名 Skill').slice(0, 80), desc }
}

const SUPER_ADMIN_EMAIL = '494161546@qq.com'
const SUPER_ADMIN_PHONE = '13136092523'

export default function UploadSkill() {
  const navigate = useNavigate()
  const location = useLocation()
  const { createSkill } = useSkills()
  const { flat } = useCategories()
  const { user } = useAuth()
  const isSuperAdmin = !!user && (user.email === SUPER_ADMIN_EMAIL || user.phone === SUPER_ADMIN_PHONE)
  const inputRef = useRef(null)
  const [dragOver, setDragOver] = useState(false)
  const [parseBusy, setParseBusy] = useState(false)
  const [submitBusy, setSubmitBusy] = useState(false)
  const [pickerFocused, setPickerFocused] = useState(false)
  const [progress, setProgress] = useState(0)
  const [error, setError] = useState('')
  // step1：选文件 + 解析；step2：补全必填表单
  const [picked, setPicked] = useState(null) // {file, parsed:{name,desc}, extra}
  const [name, setName] = useState('')
  const [desc, setDesc] = useState('')
  const [creatorName, setCreatorName] = useState('')
  const [nodeId, setNodeId] = useState(null)
  const [pickerOpen, setPickerOpen] = useState(false)

  // 支持从评测面板「采纳」后跳转过来的预填充文件
  useEffect(() => {
    const preloaded = location.state?.preloadedFile
    if (preloaded) {
      handleFiles([preloaded])
      // 清除 state 避免刷新时重复触发
      window.history.replaceState({}, '')
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  const nodePath = nodeId != null ? getNodePath(nodeId, flat) : ''

  const handleFiles = async (fileList) => {
    setError('')
    const file = fileList?.[0]
    if (!file) return
    const ext = (file.name.split('.').pop() || '').toLowerCase()
    if (!ACCEPT_EXT.includes(ext)) {
      setError(`仅支持 ${ACCEPT_EXT.join(' / ')}，当前是 .${ext}`)
      return
    }
    // 与后端/Nginx(client_max_body_size 50M) 保持一致，统一 50MB 上限
    const MAX_SIZE = 50 * 1024 * 1024
    if (file.size > MAX_SIZE) {
      setError(`文件过大（${(file.size / 1024 / 1024).toFixed(2)} MB），上限 50 MB`)
      return
    }
    try {
      setParseBusy(true)
      let parsed
      let extra = null
      if (ext === 'zip') {
        const r = await extractMainDocFromZip(file)
        if (r) {
          parsed = parseMd(r.text, file.name.replace(/\.[^.]+$/, ''))
          extra = { mainPath: r.mainPath, fileCount: r.fileCount }
        } else {
          parsed = { name: file.name.replace(/\.[^.]+$/, '').slice(0, 80), desc: '' }
        }
      } else {
        const text = await file.text()
        parsed = parseMd(text, file.name.replace(/\.[^.]+$/, ''))
      }
      setPicked({ file, parsed, extra })
      setName(parsed.name || '')
      setDesc(parsed.desc || '')
    } catch (e) {
      setError('读取文件失败：' + (e?.message || e))
    } finally {
      setParseBusy(false)
    }
  }

  const handleDrop = (e) => {
    e.preventDefault()
    setDragOver(false)
    handleFiles(e.dataTransfer?.files)
  }

  const reset = () => {
    setPicked(null)
    setName('')
    setDesc('')
    setCreatorName('')
    setNodeId(null)
    setProgress(0)
    setError('')
    if (inputRef.current) inputRef.current.value = ''
  }

  // 校验必填项
  const trimmedName = name.trim()
  const trimmedCreator = creatorName.trim()
  const canSubmit = !!picked && !!trimmedName && !!trimmedCreator && nodeId != null && !submitBusy

  const handleConfirm = async () => {
    if (!picked) { setError('请先选择文件'); return }
    if (!trimmedName) { setError('请填写 Skill 名称'); return }
    if (!trimmedCreator) { setError('请填写创建人姓名'); return }
    if (nodeId == null) { setError('请选择关联流程节点（强制归位，不允许留作孤儿）'); return }
    // 二次拦截 archived（NodePickerModal 已禁选，这里兜底）
    const target = flat.find(n => n.id === nodeId)
    if (target && target.status === 'archived') {
      setError('该流程节点已归档，不可作为新 Skill 的归属')
      return
    }
    setError('')
    try {
      setSubmitBusy(true)
      // 第 1 步：上传原始包到 R2
      setProgress(0)
      const up = await uploadPackage(picked.file, (p) => setProgress(p))
      const f = up?.data || up
      if (!f?.r2_key) {
        setError('上传失败：后端未返回文件 key')
        return
      }
      // 第 2 步：创建 Skill 并强制绑定 node + package + 创建人
      const id = await createSkill({
        name: trimmedName,
        desc: desc.trim(),
        node_id: nodeId,
        node_path: nodePath,
        source: 'upload',
        creator_name: trimmedCreator,
        package_key: f.r2_key,
        package_size: f.size ?? picked.file.size,
        package_name: f.filename ?? picked.file.name,
      })
      if (!id) { setError('创建失败：后端未返回 Skill ID'); return }
      // 上传成功后：超管可选择跳转评测，普通用户直接进编辑页
      if (isSuperAdmin) {
        const goEval = window.confirm(
          '上传成功！是否立即进行 AI 评测？\n\n' +
          '点「确定」：跳转评测页，自动启动评测（约 30 秒）\n' +
          '点「取消」：先去编辑页补全描述与详情'
        )
        if (goEval) {
          navigate('/skill-eval', { state: { autoEvalSkillId: id } })
        } else {
          navigate(`/skills/${id}/edit`)
        }
      } else {
        navigate(`/skills/${id}/edit`)
      }
    } catch (e) {
      const detail = e?.response?.data?.message || e?.response?.data?.error || e?.message || String(e)
      setError('提交失败：' + detail)
    } finally {
      setSubmitBusy(false)
    }
  }

  return (
    <div className="flex-1 overflow-auto p-8 max-w-3xl mx-auto w-full">
      <div className="flex items-center justify-between mb-4">
        <h1 className="text-[18px] font-semibold text-gray-900">上传 Skill</h1>
        <div className="text-[11px] text-gray-400">两步：选文件 → 补全归属与创建人。所有 Skill 必须挂在流程节点下，不允许成为孤儿。</div>
      </div>

      {!picked && (
        <label
          onDragOver={(e) => { e.preventDefault(); setDragOver(true) }}
          onDragLeave={() => setDragOver(false)}
          onDrop={handleDrop}
          className={[
            'relative block mt-2 border-2 border-dashed rounded-lg p-12 text-center cursor-pointer transition select-none overflow-hidden',
            dragOver || pickerFocused ? 'border-brand-500 bg-brand-50/60' : 'border-gray-300 hover:border-brand-400 hover:bg-gray-50',
          ].join(' ')}
        >
          <input
            ref={inputRef}
            type="file"
            accept={ACCEPT}
            className="absolute inset-0 z-10 h-full w-full cursor-pointer opacity-0"
            aria-label="选择 Skill 文件"
            onClick={() => setError('')}
            onFocus={() => setPickerFocused(true)}
            onBlur={() => setPickerFocused(false)}
            onChange={(e) => handleFiles(e.target.files)}
          />
          <div className="pointer-events-none">
            <div className="text-[28px] mb-2">⬆️</div>
            <div className="text-[14px] text-gray-700 mb-1">将文件拖到此处，或点击选择</div>
            <div className="text-[12px] text-gray-400">.md / .markdown / .txt / .zip ≤ 50 MB</div>
            {parseBusy && <div className="mt-3 text-[12px] text-brand-600">读取中…</div>}
          </div>
        </label>
      )}

      {picked && (
        <div className="mt-2 bg-white border border-gray-200 rounded-xl p-6">
          <div className="flex items-center justify-between mb-4">
            <div className="text-[13px] text-gray-500">
              已选文件：<span className="text-gray-800 font-medium">{picked.file.name}</span>
              <span className="ml-2 text-gray-400">{(picked.file.size / 1024).toFixed(1)} KB</span>
              {picked.extra && (
                <span className="ml-2 text-gray-400">· 主文档 {picked.extra.mainPath} · 包内 {picked.extra.fileCount} 个文件</span>
              )}
            </div>
            <button onClick={reset} className="text-[12px] text-gray-500 hover:text-gray-700">重新选择</button>
          </div>

          <div className="space-y-5">
            {/* 名称 */}
            <div>
              <label className="block text-[13px] text-gray-700 mb-1">
                名称 <span className="text-rose-500">*</span>
              </label>
              <input
                value={name}
                onChange={(e) => setName(e.target.value)}
                placeholder="例如：流程访谈助手"
                className="w-full px-3 py-2 text-[13px] border border-gray-200 rounded-md focus:outline-none focus:border-brand-400"
              />
            </div>

            {/* 描述 */}
            <div>
              <label className="block text-[13px] text-gray-700 mb-1">描述</label>
              <textarea
                rows={2}
                value={desc}
                onChange={(e) => setDesc(e.target.value)}
                placeholder="一句话说明 Skill 用途"
                className="w-full px-3 py-2 text-[13px] border border-gray-200 rounded-md focus:outline-none focus:border-brand-400"
              />
            </div>

            {/* 创建人姓名（用户自填、必填） */}
            <div>
              <label className="block text-[13px] text-gray-700 mb-1">
                创建人姓名 <span className="text-rose-500">*</span>
                <span className="ml-2 text-[11px] text-gray-400">用于让运营/同事知道这个 Skill 是谁建的</span>
              </label>
              <input
                value={creatorName}
                onChange={(e) => setCreatorName(e.target.value)}
                placeholder="请填写您的真实姓名"
                maxLength={32}
                className="w-full px-3 py-2 text-[13px] border border-gray-200 rounded-md focus:outline-none focus:border-brand-400"
              />
            </div>

            {/* 关联流程节点（强制必填） */}
            <div>
              <label className="block text-[13px] text-gray-700 mb-1">
                关联流程节点 <span className="text-rose-500">*</span>
                <span className="ml-2 text-[11px] text-gray-400">所有 Skill 必须归属到流程架构下的某个节点，已归档节点不可选</span>
              </label>
              <div
                onClick={() => setPickerOpen(true)}
                className="w-full px-3 py-2 text-[13px] border border-gray-200 rounded-md cursor-pointer hover:border-brand-400 transition flex items-center justify-between gap-2 bg-white"
              >
                {nodeId != null ? (
                  <span className="text-gray-800 truncate">
                    <span className="text-brand-600 mr-1">⇲</span>{nodePath}
                  </span>
                ) : (
                  <span className="text-gray-400">点击从流程架构选择关联节点（必选）</span>
                )}
                <span className="text-gray-300 text-[12px] flex-shrink-0">{nodeId != null ? '更换' : '选择'} ▸</span>
              </div>
            </div>

            {/* 进度条 */}
            {submitBusy && (
              <div>
                <div className="text-[12px] text-gray-500 mb-1">上传中… {progress}%</div>
                <div className="w-full h-1.5 bg-gray-100 rounded overflow-hidden">
                  <div className="h-full bg-brand-500 transition-all" style={{ width: `${progress}%` }} />
                </div>
              </div>
            )}
          </div>

          <div className="mt-5 flex items-center justify-end gap-2 border-t border-gray-100 pt-4">
            <button
              onClick={reset}
              disabled={submitBusy}
              className="px-3.5 py-1.5 text-[13px] rounded-md text-gray-600 border border-gray-200 hover:bg-gray-50 disabled:opacity-50"
            >取消</button>
            <button
              onClick={handleConfirm}
              disabled={!canSubmit}
              className="px-3.5 py-1.5 text-[13px] rounded-md text-white bg-brand-600 hover:bg-brand-700 disabled:opacity-50 disabled:cursor-not-allowed"
              title={canSubmit ? '' : '请补全名称、创建人姓名、关联流程节点后再提交'}
            >
              {submitBusy ? '提交中…' : '上传并归档 →'}
            </button>
          </div>
        </div>
      )}

      {error && (
        <div className="mt-3 text-[12px] text-rose-600 bg-rose-50 border border-rose-200 rounded-md px-3 py-2">
          {error}
        </div>
      )}

      <NodePickerModal
        open={pickerOpen}
        initialNodeId={nodeId}
        onClose={() => setPickerOpen(false)}
        onConfirm={(newId) => { setNodeId(newId); setPickerOpen(false) }}
      />
    </div>
  )
}
