/**
 * Attachment parsing utilities — pure functions extracted from conversation-engine
 * to break the agent-callback-aggregator ↔ conversation-engine circular import.
 *
 * 无闭包状态、无模块级可变状态；依赖 Node 内置 fs/path 与 IM 类型定义。
 */

import fs from 'fs'
import path from 'path'
import type { OutboundAttachment } from '../types'

/**
 * Parse MEDIA: protocol lines from Agent response text.
 * Returns cleaned text (MEDIA: lines stripped) and extracted file paths.
 */
export function parseMediaProtocol(text: string): { text: string; mediaPaths: string[] } {
  const mediaPaths: string[] = []
  const cleanedLines: string[] = []
  for (const line of text.split('\n')) {
    const trimmed = line.trim()
    if (trimmed.startsWith('MEDIA:')) {
      const filePath = trimmed.slice(6).trim()
      if (filePath) mediaPaths.push(filePath)
    } else {
      cleanedLines.push(line)
    }
  }
  // Remove trailing empty lines left by stripping MEDIA: lines
  const cleaned = cleanedLines.join('\n').replace(/\n{3,}/g, '\n\n').trim()
  return { text: cleaned, mediaPaths }
}

/** Extract file paths from file-producing tool_use blocks (Write, Bash curl/wget/cp/mv). */
export function extractFilePaths(blocks: Record<string, unknown>[]): string[] {
  const paths = new Set<string>()
  for (const block of blocks) {
    if (block.type !== 'tool_use') continue
    const name = block.name as string
    const input = block.input as Record<string, unknown>
    if (name === 'Write') {
      const fp = String(input.file_path || input.path || '')
      if (fp) paths.add(fp)
    } else if (name === 'Bash') {
      const cmd = String(input.command || '')
      const patterns = [
        /curl\s+.*?-o\s+["']?(\S+?)["']?(?:\s|$)/g,
        /wget\s+.*?-O\s+["']?(\S+?)["']?(?:\s|$)/g,
        /\bcp\s+\S+\s+["']?(\S+?)["']?\s*$/gm,
        /\bmv\s+\S+\s+["']?(\S+?)["']?\s*$/gm,
      ]
      for (const pattern of patterns) {
        let match
        while ((match = pattern.exec(cmd)) !== null) {
          paths.add(match[1])
        }
      }
    }
  }
  return [...paths]
}

const IMAGE_EXTS: Record<string, true> = {
  '.png': true, '.jpg': true, '.jpeg': true, '.gif': true, '.webp': true, '.bmp': true,
}
// Only skip source code, config, and build files — allow documents (.pdf, .doc, .ppt, .xls),
// text (.md, .txt, .csv), images, and archives
const SKIP_EXTS: Record<string, true> = {
  '.ts': true, '.tsx': true, '.js': true, '.jsx': true, '.py': true, '.go': true, '.rs': true,
  '.java': true, '.cpp': true, '.c': true, '.h': true,
  '.rb': true, '.php': true, '.swift': true, '.kt': true, '.scala': true, '.lua': true,
  '.pl': true, '.zig': true,
  '.json': true, '.yaml': true, '.yml': true, '.toml': true, '.xml': true,
  '.css': true, '.scss': true,
  '.lock': true, '.log': true, '.env': true, '.gitignore': true, '.map': true, '.d.ts': true,
  '.sh': true, '.sql': true,
}
const MIME_MAP: Record<string, string> = {
  // Images
  '.png': 'image/png', '.jpg': 'image/jpeg', '.jpeg': 'image/jpeg',
  '.gif': 'image/gif', '.webp': 'image/webp', '.bmp': 'image/bmp',
  // Documents
  '.pdf': 'application/pdf',
  '.doc': 'application/msword', '.docx': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
  '.xls': 'application/vnd.ms-excel', '.xlsx': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
  '.ppt': 'application/vnd.ms-powerpoint', '.pptx': 'application/vnd.openxmlformats-officedocument.presentationml.presentation',
  // Text & data
  '.txt': 'text/plain', '.md': 'text/markdown', '.csv': 'text/csv',
  '.json': 'application/json', '.html': 'text/html',
  // Archives
  '.zip': 'application/zip', '.tar': 'application/x-tar', '.gz': 'application/gzip',
}

/** Resolve file paths to OutboundAttachment objects. Skips source code, empty, and >20MB files. */
export function resolveFileAttachments(filePaths: string[]): OutboundAttachment[] {
  const attachments: OutboundAttachment[] = []
  for (const fp of filePaths) {
    try {
      if (!fs.existsSync(fp)) continue
      const stat = fs.statSync(fp)
      if (!stat.isFile() || stat.size === 0 || stat.size > 20 * 1024 * 1024) continue
      const ext = path.extname(fp).toLowerCase()
      if (SKIP_EXTS[ext]) continue
      attachments.push({
        filePath: fp,
        name: path.basename(fp),
        mimeType: MIME_MAP[ext] || 'application/octet-stream',
        size: stat.size,
        isImage: !!IMAGE_EXTS[ext],
      })
    } catch { /* skip unreadable files */ }
  }
  return attachments
}