import type { AssistantContentPart } from '#cli/core/model'
import { readFileSync, statSync } from 'node:fs'
import path from 'node:path'

const MAX_INLINE_PLAN_MARKDOWN_BYTES = 256 * 1024

const inlinePlanMarkdownCache = new Map<
  string,
  Readonly<{ mtimeMs: number, size: number, markdown: string | null }>
>()

function normalizeInlinePlanMarkdownPath(value: unknown): string | null {
  const candidate = String(value ?? '').trim()
  if (!candidate || candidate.includes('\n') || candidate.includes('\0'))
    return null
  if (!path.isAbsolute(candidate))
    return null
  if (path.extname(candidate).toLowerCase() !== '.md')
    return null
  return path.normalize(candidate)
}

function rememberInlinePlanMarkdown(
  normalizedPath: string,
  entry: Readonly<{ mtimeMs: number, size: number, markdown: string | null }>,
): void {
  inlinePlanMarkdownCache.delete(normalizedPath)
  inlinePlanMarkdownCache.set(normalizedPath, entry)
  while (inlinePlanMarkdownCache.size > 32) {
    const oldestKey = inlinePlanMarkdownCache.keys().next().value
    if (!oldestKey)
      break
    inlinePlanMarkdownCache.delete(oldestKey)
  }
}

export function readInlinePlanMarkdownSync(filePath: string): string | null {
  const normalizedPath = normalizeInlinePlanMarkdownPath(filePath)
  if (!normalizedPath)
    return null

  try {
    const fileInfo = statSync(normalizedPath)
    if (
      !fileInfo.isFile()
      || fileInfo.size <= 0
      || fileInfo.size > MAX_INLINE_PLAN_MARKDOWN_BYTES
    ) {
      inlinePlanMarkdownCache.delete(normalizedPath)
      return null
    }

    const cached = inlinePlanMarkdownCache.get(normalizedPath)
    if (
      cached
      && cached.mtimeMs === fileInfo.mtimeMs
      && cached.size === fileInfo.size
    ) {
      return cached.markdown
    }

    const markdown = readFileSync(normalizedPath, 'utf-8')
    const nextEntry = {
      mtimeMs: fileInfo.mtimeMs,
      size: fileInfo.size,
      markdown: markdown.trim() ? markdown : null,
    } as const
    rememberInlinePlanMarkdown(normalizedPath, nextEntry)
    return nextEntry.markdown
  }
  catch {
    inlinePlanMarkdownCache.delete(normalizedPath)
    return null
  }
}

export function extractInlinePlanMarkdownPathFromToolResult(
  result: Extract<AssistantContentPart, { type: 'tool_result' }>['result'],
): string | null {
  const structured
    = result.structuredContent && typeof result.structuredContent === 'object'
      ? result.structuredContent as Record<string, unknown>
      : {}
  const candidates = [
    structured.markdownPath,
    structured.markdown_path,
    structured.filePath,
    structured.file_path,
    structured.path,
    result.output,
  ]

  for (const candidate of candidates) {
    const normalized = normalizeInlinePlanMarkdownPath(candidate)
    if (normalized)
      return normalized
  }

  return null
}
