import {
  isEditLikeToolName,
  isReadLikeToolName,
  isSearchLikeToolName,
  isWriteLikeToolName,
} from '#cli/core/tool-metadata'
import { t } from '#cli/i18n'
import { sanitizeTerminalText } from './sanitize-terminal-text'
import { truncateWithEllipsis } from './text-layout'
import { isBashTool, normalizeToolName } from './tool-rendering'

export function getToolOutputTailLines(part: unknown): string[] | null {
  const p: any = part as any
  const v = p?.__tailLines
  if (!Array.isArray(v)) return null
  return v.map((s: any) => String(s ?? ''))
}

function lastNonEmptyLine(lines: readonly string[]): string | null {
  for (let i = lines.length - 1; i >= 0; i--) {
    const trimmed = String(lines[i] ?? '').trim()
    if (trimmed) return trimmed
  }
  return null
}

export function isExploreMarkerLine(line: string): boolean {
  const trimmed = String(line ?? '').trim()
  if (!trimmed) return false
  const lower = trimmed.toLowerCase()
  return (
    lower.startsWith('[tool]') ||
    lower.startsWith('[output]') ||
    lower.startsWith('[result]') ||
    lower.startsWith('[stderr]')
  )
}

export function isExploreToolStartLine(line: string): boolean {
  const trimmed = String(line ?? '').trim()
  if (!trimmed) return false
  return trimmed.toLowerCase().startsWith('[tool]')
}

export function findLastExploreMarkerLineIndex(lines: readonly string[]): number {
  for (let i = lines.length - 1; i >= 0; i--) {
    if (isExploreMarkerLine(lines[i] ?? '')) return i
  }
  return -1
}

function findLastExploreToolStartLineIndex(lines: readonly string[]): number {
  for (let i = lines.length - 1; i >= 0; i--) {
    if (isExploreToolStartLine(lines[i] ?? '')) return i
  }
  return -1
}

export function findExploreToolStartLineIndices(lines: readonly string[]): number[] {
  const out: number[] = []
  for (let i = 0; i < lines.length; i++) {
    if (isExploreToolStartLine(lines[i] ?? '')) out.push(i)
  }
  return out
}

export function getExploreCollapsedPreviewLines(part: unknown): string[] {
  if (!part) return []
  const tailLines = getToolOutputTailLines(part)
  const lines
    = tailLines && tailLines.length > 0
      ? tailLines
      : String((part as any)?.output ?? '')
          .replace(/\r/g, '')
          .split('\n')
  if (lines.length === 0) return []

  const idx = findLastExploreToolStartLineIndex(lines)
  const fallbackIdx = idx >= 0 ? idx : findLastExploreMarkerLineIndex(lines)
  const primaryRaw
    = fallbackIdx >= 0
      ? String(lines[fallbackIdx] ?? '')
      : (lastNonEmptyLine(lines) ?? '')
  const primary = primaryRaw.trim()
  if (!primary) return []

  const formatPreviewLine = (raw: string): string => {
    const t = String(raw ?? '').trim()
    if (!t) return ''
    if (isExploreToolStartLine(t)) {
      const parsed = parseExploreToolLine(t)
      if (!parsed) return sanitizeTerminalText(t)
      const suffix = summarizeExploreToolArgs(parsed.toolName, parsed.argsText)
      const display = suffix
        ? `[tool] ${parsed.toolName} ${suffix}`
        : `[tool] ${parsed.toolName}`
      return sanitizeTerminalText(display)
    }
    return sanitizeTerminalText(t)
  }

  let secondary = ''
  if (fallbackIdx >= 0) {
    for (let j = fallbackIdx + 1; j < lines.length; j++) {
      const t = String(lines[j] ?? '').trim()
      if (!t) continue
      if (isExploreMarkerLine(t)) continue
      secondary = t
      break
    }
    if (!secondary) {
      for (let j = fallbackIdx + 1; j < lines.length; j++) {
        const t = String(lines[j] ?? '').trim()
        if (!t) continue
        if (isExploreToolStartLine(t)) continue
        if (isExploreMarkerLine(t)) {
          secondary = t
          break
        }
      }
    }
  }
  if (!secondary) {
    for (let j = lines.length - 1; j >= 0; j--) {
      const t = String(lines[j] ?? '').trim()
      if (!t || t === primary) continue
      if (isExploreMarkerLine(t)) continue
      secondary = t
      break
    }
  }

  const out = [formatPreviewLine(primary)]
  if (secondary) out.push(formatPreviewLine(secondary))
  return out
}

export function parseExploreToolLine(
  line: string,
): { toolName: string, argsText: string, callId: string } | null {
  const raw = String(line ?? '').trim()
  if (!raw) return null
  if (!raw.toLowerCase().startsWith('[tool]')) return null
  const after = raw.slice(6).trim()
  if (!after) return null
  const firstSpace = after.indexOf(' ')
  if (firstSpace < 0) return { toolName: after, argsText: '', callId: '' }
  const toolName = after.slice(0, firstSpace).trim()
  const argsText = after.slice(firstSpace + 1).trim()
  const parsed = safeParseExploreArgs(argsText)
  const callId
    = typeof (parsed as any)?._call_id === 'string'
      ? String((parsed as any)._call_id).trim()
      : ''
  return { toolName, argsText, callId }
}

export function parseExploreNamedMarkerLine(line: string): {
  kind: 'result' | 'output' | 'stderr'
  toolName: string
  callId: string
} | null {
  const raw = String(line ?? '').trim()
  if (!raw) return null
  const m = raw.match(/^\[(result|output|stderr)\]\s+(\S+)(?:\s+(\S+))?/i)
  if (!m) return null
  const kind = String(m[1] ?? '').toLowerCase()
  const toolName = String(m[2] ?? '').trim()
  const callId = String(m[3] ?? '').trim()
  if (!toolName) return null
  if (kind === 'result' || kind === 'output' || kind === 'stderr')
    return { kind, toolName, callId }
  return null
}

export function safeParseExploreArgs(argsText: string): any | null {
  const t = String(argsText ?? '').trim()
  if (!t) return null
  if (
    (t.startsWith('{') || t.startsWith('[')) &&
    !t.endsWith('...') &&
    !t.endsWith('…')
  ) {
    try {
      return JSON.parse(t)
    }
    catch {
      return null
    }
  }
  return null
}

export function summarizeExploreToolArgs(toolName: string, argsText: string): string {
  const lower = normalizeToolName(toolName)
  const parsed = safeParseExploreArgs(argsText)
  const argsObj
    = parsed && typeof parsed === 'object' && !Array.isArray(parsed)
      ? (parsed as any)
      : null

  if (isReadLikeToolName(lower)) {
    const filePath
      = typeof argsObj?.path === 'string'
        ? String(argsObj.path).trim()
        : typeof argsObj?.file_path === 'string'
          ? String(argsObj.file_path).trim()
          : ''
    if (filePath) {
      const limit = argsObj?.limit ?? argsObj?.endLine
      const offset = argsObj?.offset ?? argsObj?.startLine
      const suffixParts: string[] = []
      if (typeof offset === 'number' && offset > 0)
        suffixParts.push(t('chatRender.toolSuffix.offset', { value: offset }))
      if (typeof limit === 'number' && limit > 0)
        suffixParts.push(t('chatRender.toolSuffix.limit', { value: limit }))
      return suffixParts.length > 0
        ? `${filePath} (${suffixParts.join(', ')})`
        : filePath
    }
  }
  if (isSearchLikeToolName(lower)) {
    const query
      = typeof argsObj?.query === 'string'
        ? String(argsObj.query).trim()
        : typeof argsObj?.pattern === 'string'
          ? String(argsObj.pattern).trim()
          : ''
    if (query) return query
  }
  if (isWriteLikeToolName(lower)) {
    const filePath
      = typeof argsObj?.path === 'string'
        ? String(argsObj.path).trim()
        : typeof argsObj?.file_path === 'string'
          ? String(argsObj.file_path).trim()
          : ''
    if (filePath) return filePath
  }
  if (isEditLikeToolName(lower)) {
    const filePath
      = typeof argsObj?.path === 'string'
        ? String(argsObj.path).trim()
        : typeof argsObj?.file_path === 'string'
          ? String(argsObj.file_path).trim()
          : ''
    if (filePath) {
      const fileName = filePath.split(/[/\\]/).filter(Boolean).pop() ?? filePath
      return fileName
    }
  }
  if (isBashTool(lower)) {
    const description
      = typeof argsObj?.description === 'string'
        ? String(argsObj.description).trim()
        : ''
    if (description) return description
    const command
      = typeof argsObj?.command === 'string' ? String(argsObj.command).trim() : ''
    if (command) return truncateWithEllipsis(command.replace(/\s+/g, ' '), 60)
  }
  if (lower === 'websearch') {
    const query
      = typeof argsObj?.query === 'string' ? String(argsObj.query).trim() : ''
    if (query) return query
  }

  return ''
}
