function isRecord(value: unknown): value is Record<string, unknown> {
  return value != null && typeof value === 'object' && !Array.isArray(value)
}

function extractTextLinesFromJsonish(seg: string): string[] | null {
  const raw = String(seg ?? '').trim()
  if (!raw)
    return null
  try {
    const parsed = JSON.parse(raw)
    const items = Array.isArray(parsed) ? parsed : [parsed]
    const out: string[] = []
    for (const item of items) {
      if (!isRecord(item) || item.type !== 'text')
        continue

      const text = String(item.text ?? '').replace(/\r/g, '')
      if (text)
        out.push(...text.split('\n'))
    }
    return out.length > 0 ? out : null
  }
  catch {
    return null
  }
}

function takeFirstLinesFast(
  text: string,
  maxLines: number,
): { head: string, truncated: boolean } {
  const raw = String(text ?? '').replace(/\r/g, '')
  if (maxLines <= 0)
    return { head: '', truncated: raw.length > 0 }

  let linesSeen = 1
  for (let i = 0; i < raw.length; i++) {
    if (raw.charCodeAt(i) === 10) {
      // '\n'
      linesSeen++
      if (linesSeen > maxLines) {
        return { head: raw.slice(0, i), truncated: true }
      }
    }
  }
  return { head: raw, truncated: false }
}

export function truncateReadToolOutput(
  output: string,
  opts?: Readonly<{
    maxLines?: number
    maxJsonParseChars?: number
    ellipsisLine?: string
  }>,
): { output: string, truncated: boolean } {
  const maxLines = Math.max(1, Math.floor(opts?.maxLines ?? 10))
  const maxJsonParseChars = Math.max(
    0,
    Math.floor(opts?.maxJsonParseChars ?? 200_000),
  )
  const ellipsisLine = String(opts?.ellipsisLine ?? '...')

  const raw = String(output ?? '').replace(/\r/g, '')
  if (!raw.trim())
    return { output: raw, truncated: false }

  // Prefer JSONish "text" extraction when it's small enough to parse safely.
  if (raw.length <= maxJsonParseChars) {
    const jsonLines = extractTextLinesFromJsonish(raw)
    if (jsonLines && jsonLines.length > 0) {
      const shown = jsonLines.slice(0, maxLines).join('\n')
      const truncated = jsonLines.length > maxLines
      return {
        output: truncated ? `${shown}\n${ellipsisLine}` : shown,
        truncated,
      }
    }
  }

  const taken = takeFirstLinesFast(raw, maxLines)
  return {
    output: taken.truncated ? `${taken.head}\n${ellipsisLine}` : taken.head,
    truncated: taken.truncated,
  }
}
