const QUESTION_FENCE_RE = /(^|\n)```acw-question[ \t]*\r?\n([\s\S]*?)\r?\n```(?=\n|$)/gi

export type InteractiveQuestionMode = 'single' | 'multiple'

export interface InteractiveQuestionOption {
  id: string
  label: string
  summary?: string
  details?: string
}

export interface InteractiveQuestion {
  version: 1
  id: string
  title: string
  description?: string
  mode: InteractiveQuestionMode
  options: InteractiveQuestionOption[]
  allowCustom: boolean
}

export interface ParsedInteractiveMessage {
  markdown: string
  question: InteractiveQuestion | null
}

export interface InteractiveQuestionAnswer {
  question: InteractiveQuestion
  selectedOptionIds: string[]
  customText: string
}

interface FenceMatch {
  raw: string
  json: string
}

export function parseInteractiveMessage(content: string): ParsedInteractiveMessage {
  const matches = findQuestionFences(content)
  for (const match of matches) {
    const question = parseQuestionJSON(match.json)
    if (!question) continue
    return {
      markdown: content.replace(match.raw, '').trim(),
      question,
    }
  }
  return { markdown: content, question: null }
}

export function formatInteractiveQuestionAnswer(answer: InteractiveQuestionAnswer): string {
  const selected = answer.selectedOptionIds
    .map((id) => answer.question.options.find((option) => option.id === id))
    .filter((option): option is InteractiveQuestionOption => option != null)

  const selectedLines =
    selected.length > 0
      ? selected.map((option) => {
          const summary = option.summary ? ` - ${option.summary}` : ''
          return `- ${option.id}: ${option.label}${summary}`
        })
      : ['- none']

  const customText = answer.customText.trim() || '无'
  return [
    '我选择了结构化问题的答案。',
    '',
    `question_id: ${answer.question.id}`,
    `question: ${answer.question.title}`,
    'selected_options:',
    ...selectedLines,
    '',
    '补充说明:',
    customText,
  ].join('\n')
}

function findQuestionFences(content: string): FenceMatch[] {
  const matches: FenceMatch[] = []
  QUESTION_FENCE_RE.lastIndex = 0
  let match: RegExpExecArray | null
  while ((match = QUESTION_FENCE_RE.exec(content)) != null) {
    matches.push({ raw: match[0], json: match[2] })
  }
  return matches
}

function parseQuestionJSON(json: string): InteractiveQuestion | null {
  let value: unknown
  try {
    value = JSON.parse(json)
  } catch {
    return null
  }
  return normalizeQuestion(value)
}

function normalizeQuestion(value: unknown): InteractiveQuestion | null {
  const record = asRecord(value)
  if (!record) return null
  if (record.version !== 1) return null

  const id = requiredString(record.id)
  const title = requiredString(record.title)
  const mode = record.mode === 'single' || record.mode === 'multiple' ? record.mode : null
  if (!id || !title || !mode || !Array.isArray(record.options)) return null

  const options: InteractiveQuestionOption[] = []
  for (const optionValue of record.options) {
    const option = normalizeOption(optionValue)
    if (!option) return null
    options.push(option)
  }
  if (options.length < 2 || options.length > 5) return null

  const optionIds = new Set(options.map((option) => option.id))
  if (optionIds.size !== options.length) return null

  return {
    version: 1,
    id,
    title,
    description: optionalString(record.description),
    mode,
    options,
    allowCustom: record.allowCustom === true,
  }
}

function normalizeOption(value: unknown): InteractiveQuestionOption | null {
  const record = asRecord(value)
  if (!record) return null

  const id = requiredString(record.id)
  const label = requiredString(record.label)
  if (!id || !label) return null

  return {
    id,
    label,
    summary: optionalString(record.summary),
    details: optionalString(record.details),
  }
}

function asRecord(value: unknown): Record<string, unknown> | null {
  if (value == null || typeof value !== 'object' || Array.isArray(value)) return null
  return value as Record<string, unknown>
}

function requiredString(value: unknown): string | null {
  if (typeof value !== 'string') return null
  const trimmed = value.trim()
  return trimmed === '' ? null : trimmed
}

function optionalString(value: unknown): string | undefined {
  if (typeof value !== 'string') return undefined
  const trimmed = value.trim()
  return trimmed === '' ? undefined : trimmed
}
