import JSZip from 'jszip'

const FENCE_RE = /^[ \t]*(```|~~~)([^\n]*)\n([\s\S]*?)^[ \t]*\1[ \t]*$/gm
const PATH_ATTR_RE = /(?:filename|file|path|title)\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s`]+))/i
const PROSE_PATH_RE = /[`"'“”‘’]?([^\s`"'“”‘’：:，,；;、]+(?:\/[^\s`"'“”‘’：:，,；;、]+)*\.[A-Za-z0-9]{1,12})[`"'“”‘’]?\s*[：:]\s*$/u
const PLAIN_FILE_LABEL_RE = /^\s*(?:(?:[-*+]|\d+[.)、])\s*)?(?:\*\*|__)?\s*([^\s`"'“”‘’：:，,；;、*]+(?:\/[^\s`"'“”‘’：:，,；;、*]+)*\.(?:md|markdown|txt|json|ya?ml|csv))\s*[：:]?\s*(?:\*\*|__)?\s*$/iu

function safePath(value) {
  const path = String(value || '')
    .trim()
    .replace(/^['"`]+|['"`]+$/g, '')
    .replace(/\\/g, '/')
    .replace(/^\/+/, '')
  if (!path || path.length > 240 || /[\0-\x1f]/.test(path)) return ''
  if (/^[A-Za-z]:\//.test(path) || /^[a-z][a-z0-9+.-]*:\/\//i.test(path)) return ''
  const parts = path.split('/').filter(Boolean)
  if (!parts.length || parts.some(part => part === '.' || part === '..' || part.startsWith('.') || part.length > 120)) return ''
  const normalized = parts.join('/')
  return normalized
}

function withTrailingNewline(content) {
  const text = String(content || '').replace(/\r\n/g, '\n').replace(/\s+$/g, '')
  return text ? `${text}\n` : ''
}

function isPlaceholderFileContent(content) {
  const raw = String(content || '')
  if (/"title"\s*:\s*"\.\.\."/.test(raw) && /"name"\s*:\s*"lowercase-skill-name"/.test(raw)) return true
  const compact = raw
    .trim()
    .replace(/\s+/g, '')
    .replace(/[。；;]+$/g, '')
  return compact === '...' || compact === '#...' || compact === '#...```'
}

function fencePathFromInfo(info) {
  const text = String(info || '')
  const attr = text.match(PATH_ATTR_RE)
  if (attr) return safePath(attr[1] || attr[2] || attr[3])

  const bare = text.match(/(?:^|\s)([^\s`"'=<>|?*]+\.[A-Za-z0-9]{1,12})(?:\s|$)/)
  return bare ? safePath(bare[1]) : ''
}

function fencePathFromProse(beforeFence) {
  const lines = String(beforeFence || '').replace(/\r/g, '').split('\n')
  const lastLine = [...lines].reverse().find(line => line.trim()) || ''
  const match = lastLine.match(PROSE_PATH_RE)
  return match ? safePath(match[1]) : ''
}

function normalizeEntryContent(content) {
  if (typeof content === 'string') return withTrailingNewline(content)
  return content
}

function plainFileSections(text) {
  const lines = String(text || '').replace(/\r/g, '').split('\n')
  const markers = []
  lines.forEach((line, index) => {
    const match = line.match(PLAIN_FILE_LABEL_RE)
    const path = match ? safePath(match[1]) : ''
    if (path) markers.push({ path, index })
  })
  const sections = []
  for (let i = 0; i < markers.length; i++) {
    const marker = markers[i]
    const next = markers[i + 1]?.index ?? lines.length
    const content = withTrailingNewline(lines.slice(marker.index + 1, next).join('\n'))
    if (content && !isPlaceholderFileContent(content)) sections.push({ path: marker.path, content })
  }
  return sections
}

function ensureMarkdownTitle(name, content) {
  const text = String(content || '').trim()
  if (/^#\s+/.test(text)) return text.endsWith('\n') ? text : `${text}\n`
  const title = String(name || '未命名 Skill').trim() || '未命名 Skill'
  return `# ${title}\n\n${text || '（暂无内容）'}\n`
}

export function safeSkillPackageFilename(name) {
  const safe = String(name || '')
    .trim()
    .replace(/[^\w\u4e00-\u9fff.-]+/g, '_')
    .replace(/^_+|_+$/g, '')
    .slice(0, 80)
  return `${safe || 'skill-package'}.zip`
}

function normalizeInputEntries(entries) {
  const out = []
  const seen = new Set()
  for (const entry of entries || []) {
    const path = safePath(entry?.path)
    if (!path || seen.has(path)) continue
    const content = normalizeEntryContent(entry?.content ?? '')
    if (content == null || content === '' || isPlaceholderFileContent(content)) continue
    out.push({ ...entry, path, content })
    seen.add(path)
  }
  return out
}

export function buildSkillPackageEntries(input = {}) {
  if (Array.isArray(input.entries)) {
    const normalized = normalizeInputEntries(input.entries)
    if (normalized.some(entry => entry.path.toLowerCase() === 'skill.md')) return normalized
    return [{ path: 'SKILL.md', content: ensureMarkdownTitle(input.name, input.description) }, ...normalized]
  }

  const { name, description } = input
  const entries = [{ path: 'SKILL.md', content: ensureMarkdownTitle(name, description) }]
  const seen = new Set(['SKILL.md'])
  const text = String(description || '')
  let skillFromFence = false

  let match
  while ((match = FENCE_RE.exec(text)) !== null) {
    const path = fencePathFromInfo(match[2]) || fencePathFromProse(text.slice(0, match.index))
    if (!path) continue
    const fileContent = withTrailingNewline(match[3])
    if (!fileContent || isPlaceholderFileContent(fileContent)) continue

    if (path.toLowerCase() === 'skill.md') {
      entries[0] = { path: 'SKILL.md', content: fileContent }
      skillFromFence = true
      continue
    }
    if (seen.has(path)) continue
    entries.push({ path, content: fileContent })
    seen.add(path)
  }

  for (const section of plainFileSections(text)) {
    if (section.path.toLowerCase() === 'skill.md') {
      if (!skillFromFence) entries[0] = { path: 'SKILL.md', content: section.content }
      continue
    }
    if (seen.has(section.path)) continue
    entries.push(section)
    seen.add(section.path)
  }

  return entries
}

export async function buildSkillPackageBlob(input = {}) {
  const zip = new JSZip()
  buildSkillPackageEntries(input).forEach(entry => {
    zip.file(entry.path, entry.content)
  })
  const blob = await zip.generateAsync({ type: 'blob', compression: 'DEFLATE' })
  return blob
}
