import { trimSpacesOnly } from './components/ChatItem/markdownFormatter'
import { isWorkflowFormAttachment } from './lib/workflowCardForm'
import type { Answer, Attachment, ChatRecord, OriginalRecord } from '@/types/chat'

export interface WorkflowControlPayload {
  recordType?: string
  question?: unknown
  end?: boolean
  /** 为 false / "false" 时表示仅作路由：用新 recordType+question 静默续问，不在气泡展示本条 answer */
  showFlag?: string | boolean | number | null
}

export interface WorkflowParseOptions {
  /** 当前流式分片的 answer 字段，用于 L4 兜底解析 */
  answerChunk?: string
  /** 当前包元数据携带 end:true 时补全解析结果中的 end */
  packetEnd?: boolean
}

/** end 字段兼容 boolean true 与字符串 "true" */
export function isWorkflowControlEnd(end: unknown): boolean {
  if (end === true)
    return true
  if (typeof end === 'string' && end.trim().toLowerCase() === 'true')
    return true
  return false
}

function isShowFlagHidden(showFlag: WorkflowControlPayload['showFlag']): boolean {
  return showFlag === false
    || showFlag === 0
    || String(showFlag ?? '').trim().toLowerCase() === 'false'
}

/** answer 内 JSON 且 showFlag 为 false：静默续问，不展示本条回答 */
export function isHiddenWorkflowRedirectPayload(parsed: WorkflowControlPayload | null | undefined): boolean {
  if (!parsed?.recordType || String(parsed.recordType).trim() === '')
    return false
  if (parsed.question == null || String(parsed.question).trim() === '')
    return false
  return isShowFlagHidden(parsed.showFlag)
}

export function isHiddenWorkflowRedirectControl(text: string, options?: WorkflowParseOptions): boolean {
  return isHiddenWorkflowRedirectPayload(tryParseWorkflowControlJson(text, options))
}

function unescapeJsonStringValue(value: string): string {
  return value.replace(/\\"/g, '"').replace(/\\\\/g, '\\')
}

/** L2：去外层引号并还原常见转义 */
function normalizeEscaped(text: string): string {
  let normalized = text.trim()
  if (
    (normalized.startsWith('"') && normalized.endsWith('"'))
    || (normalized.startsWith('\'') && normalized.endsWith('\''))
  ) {
    normalized = normalized.slice(1, -1)
  }
  return unescapeJsonStringValue(normalized)
}

function splitTopLevelJsonObjects(text: string): string[] {
  const results: string[] = []
  let depth = 0
  let start = -1
  for (let i = 0; i < text.length; i++) {
    const ch = text[i]
    if (ch === '{') {
      if (depth === 0)
        start = i
      depth++
    }
    else if (ch === '}') {
      depth--
      if (depth === 0 && start >= 0) {
        results.push(text.slice(start, i + 1))
        start = -1
      }
    }
  }
  return results
}

function collectParseCandidates(text: string): string[] {
  const trimmed = text.trim()
  if (!trimmed)
    return []

  const candidates: string[] = [trimmed]
  const fenceStart = trimmed.indexOf('```')
  if (fenceStart >= 0) {
    const lineEnd = trimmed.indexOf('\n', fenceStart)
    const fenceEnd = trimmed.indexOf('```', lineEnd >= 0 ? lineEnd + 1 : fenceStart + 3)
    if (lineEnd >= 0 && fenceEnd > lineEnd)
      candidates.push(trimmed.slice(lineEnd + 1, fenceEnd).trim())
  }

  const brace = trimmed.match(/\{[\s\S]*\}/)
  if (brace?.[0])
    candidates.push(brace[0].trim())

  for (const segment of splitTopLevelJsonObjects(trimmed))
    candidates.push(segment.trim())

  const withNormalized: string[] = []
  for (const candidate of candidates) {
    withNormalized.push(candidate)
    const normalized = normalizeEscaped(candidate)
    if (normalized !== candidate)
      withNormalized.push(normalized)
  }

  return [...new Set(withNormalized.filter(Boolean))]
}

function normalizeWorkflowPayload(raw: Record<string, unknown>): WorkflowControlPayload | null {
  const recordType = raw.recordType != null ? String(raw.recordType).trim() : ''
  const hasQuestion = raw.question != null && String(raw.question).trim() !== ''
  const hasEnd = raw.end !== undefined && raw.end !== null
  const hasShowFlag = raw.showFlag !== undefined && raw.showFlag !== null

  if (!recordType && !hasQuestion && !hasEnd && !hasShowFlag)
    return null

  return {
    ...(recordType ? { recordType } : {}),
    ...(raw.question !== undefined ? { question: raw.question } : {}),
    ...(hasEnd ? { end: isWorkflowControlEnd(raw.end) } : {}),
    ...(hasShowFlag ? { showFlag: raw.showFlag as WorkflowControlPayload['showFlag'] } : {}),
  }
}

/** L1：JSON.parse，若结果仍为 string 再 parse 一次 */
function parseJsonLevel1(text: string): WorkflowControlPayload | null {
  try {
    let parsed: unknown = JSON.parse(text)
    if (typeof parsed === 'string') {
      const inner = parsed.trim()
      if (!inner)
        return null
      parsed = JSON.parse(inner)
    }
    if (parsed && typeof parsed === 'object' && !Array.isArray(parsed))
      return normalizeWorkflowPayload(parsed as Record<string, unknown>)
  }
  catch {
    // fall through
  }
  return null
}

function extractQuestionByRegex(text: string): string | undefined {
  const quoted = text.match(/"question"\s*:\s*"((?:\\.|[^"\\])*)"/)
  if (quoted?.[1]) {
    const value = unescapeJsonStringValue(quoted[1]).trim()
    if (value)
      return value
  }
  // 兼容非法 JSON：question 未加引号，如 "question":客群洞察
  const unquoted = text.match(/"question"\s*:\s*([^,}\]]+)/)
  if (unquoted?.[1]) {
    const value = unquoted[1].trim().replace(/^["']|["']$/g, '')
    if (value)
      return value
  }
  return undefined
}

/** L3：正则提取 recordType / question / end / showFlag */
function extractByRegex(text: string): WorkflowControlPayload | null {
  const recordTypeMatch = text.match(/"recordType"\s*:\s*"((?:\\.|[^"\\])*)"/)
  const questionFromRegex = extractQuestionByRegex(text)
  const endMatch = text.match(/"end"\s*:\s*(true|false|"true"|"false")/i)
  const showFlagMatch = text.match(/"showFlag"\s*:\s*(false|true|"false"|"true"|0|1)/i)

  if (!recordTypeMatch && !questionFromRegex && !endMatch)
    return null

  const payload: WorkflowControlPayload = {}
  if (recordTypeMatch?.[1])
    payload.recordType = unescapeJsonStringValue(recordTypeMatch[1]).trim()
  if (questionFromRegex)
    payload.question = questionFromRegex
  if (endMatch?.[1])
    payload.end = isWorkflowControlEnd(endMatch[1].replace(/"/g, ''))
  if (showFlagMatch?.[1]) {
    const flag = showFlagMatch[1].replace(/"/g, '').toLowerCase()
    payload.showFlag = flag === 'false' || flag === '0' ? false : flag
  }
  return payload
}

/** 合并多段解析结果，优先保留更完整的 recordType / question */
function mergeWorkflowControlPayload(
  ...parts: Array<WorkflowControlPayload | null | undefined>
): WorkflowControlPayload | null {
  const merged: WorkflowControlPayload = {}
  for (const part of parts) {
    if (!part)
      continue
    if (part.recordType != null && String(part.recordType).trim())
      merged.recordType = String(part.recordType).trim()
    if (part.question != null && String(part.question).trim())
      merged.question = part.question
    if (part.end === true)
      merged.end = true
    if (part.showFlag !== undefined && part.showFlag !== null)
      merged.showFlag = part.showFlag
  }
  if (!merged.recordType && merged.question == null && merged.end !== true && merged.showFlag == null)
    return null
  return merged
}

function mergePacketEnd(
  payload: WorkflowControlPayload | null,
  options?: WorkflowParseOptions,
): WorkflowControlPayload | null {
  if (!payload)
    return null
  if (options?.packetEnd && payload.end !== true)
    return { ...payload, end: true }
  return payload
}

function parseWorkflowControlFromText(text: string): WorkflowControlPayload | null {
  const candidates = collectParseCandidates(text)
  let best: WorkflowControlPayload | null = null

  for (const candidate of candidates) {
    const merged = mergeWorkflowControlPayload(
      parseJsonLevel1(candidate),
      parseJsonLevel1(normalizeEscaped(candidate)),
      extractByRegex(candidate),
    )
    if (!merged)
      continue
    if (isWorkflowEnterSubPayload(merged) || isHiddenWorkflowRedirectPayload(merged))
      return merged
    best = mergeWorkflowControlPayload(best, merged)
  }

  return mergeWorkflowControlPayload(best, extractByRegex(text))
}

/** 兼容 API 返回 workFlowSessionId / workflowSessionId 等字段名 */
export function resolveWorkFlowSessionId(
  data: Record<string, unknown> | Answer | null | undefined,
  fallback?: string | null,
): string | undefined {
  if (data && typeof data === 'object') {
    const record = data as Record<string, unknown>
    for (const key of ['workFlowSessionId', 'workflowSessionId', 'work_flow_session_id']) {
      const val = record[key]
      if (val != null && String(val).trim())
        return String(val).trim()
    }
  }
  const fb = fallback != null ? String(fallback).trim() : ''
  return fb || undefined
}

/** 流式分包元数据是否携带工作流结束标识 */
export function isWorkflowPacketEnd(data: Record<string, unknown> | null | undefined): boolean {
  if (!data)
    return false
  if (isWorkflowControlEnd(data.end))
    return true
  if (data.endAnswer === true)
    return true
  if (data.endAnswerFlag === true)
    return true
  return false
}

/**
 * 四层解析主/子工作流控制 JSON：
 * L1 JSON.parse（含双层字符串）
 * L2 normalizeEscaped 后再 parse
 * L3 extractByRegex 字段兜底
 * L4 对当前包 answerChunk 重复上述步骤，并用 packetEnd 补全 end
 */
export function tryParseWorkflowControlJson(text: string, options?: WorkflowParseOptions): WorkflowControlPayload | null {
  const sources = [text]
  if (options?.answerChunk?.trim())
    sources.push(options.answerChunk)

  for (const source of sources) {
    const parsed = parseWorkflowControlFromText(source)
    const merged = mergePacketEnd(parsed, options)
    if (merged)
      return merged
  }

  return null
}

/** 流式分片 answer 可能是 string / 双层 JSON 字符串 / 对象 */
export function normalizeStreamAnswerChunk(raw: unknown): string {
  if (raw == null)
    return ''
  if (typeof raw === 'string')
    return raw
  if (typeof raw === 'object') {
    try {
      return JSON.stringify(raw)
    }
    catch {
      return ''
    }
  }
  return String(raw)
}

/** 是否为「进入子工作流」控制载荷（含 recordType + question，且非隐藏路由） */
export function isWorkflowEnterSubPayload(parsed: WorkflowControlPayload | null | undefined): boolean {
  if (!parsed || isHiddenWorkflowRedirectPayload(parsed))
    return false
  return Boolean(
    parsed.recordType != null
    && String(parsed.recordType).trim()
    && parsed.question != null,
  )
}

/**
 * 从流式 AI 回答解析工作流控制 JSON：
 * 优先 workflowParseText（原始累积文本），再 answer；解析失败时不用外层 recordType 误代替 answer 内 JSON。
 */
export function resolveWorkflowControlFromStreamAnswer(
  snapshot: {
    workflowParseText?: string
    answer?: string
  },
  options?: WorkflowParseOptions,
): WorkflowControlPayload | null {
  const parseText = trimSpacesOnly(
    snapshot.workflowParseText ?? snapshot.answer ?? '',
  )
  const parsedFromText = parseText
    ? tryParseWorkflowControlJson(parseText, options)
    : null
  if (parsedFromText)
    return parsedFromText

  if (options?.answerChunk?.trim()) {
    const chunkText = trimSpacesOnly(normalizeStreamAnswerChunk(options.answerChunk))
    if (chunkText)
      return tryParseWorkflowControlJson(chunkText, options)
  }
  return null
}

export function resolveWorkflowControlFromPacketAnswer(
  rawAnswer: unknown,
  options?: WorkflowParseOptions,
): WorkflowControlPayload | null {
  const chunk = normalizeStreamAnswerChunk(rawAnswer)
  if (!chunk.trim())
    return null
  return tryParseWorkflowControlJson(chunk, options)
}

/** 外层 data.question 或累积 answer 文本中的 question 字段，补全控制 JSON */
export function enrichWorkflowControlWithPacketQuestion(
  parsed: WorkflowControlPayload | null | undefined,
  packetQuestion?: unknown,
  parseText?: string,
): WorkflowControlPayload | null {
  if (!parsed)
    return null
  const fromParsed = parsed.question != null ? String(parsed.question).trim() : ''
  if (fromParsed)
    return parsed
  const fromPacket = packetQuestion != null ? String(packetQuestion).trim() : ''
  if (fromPacket)
    return { ...parsed, question: fromPacket }
  const text = parseText?.trim() ?? ''
  if (text) {
    const fromText = extractQuestionByRegex(text)
    if (fromText)
      return { ...parsed, question: fromText }
  }
  return parsed
}

export function isWorkflowControlPayloadComplete(parsed: WorkflowControlPayload | null | undefined): boolean {
  if (!parsed)
    return false
  if (isHiddenWorkflowRedirectPayload(parsed))
    return true
  return isWorkflowEnterSubPayload(parsed)
}

/**
 * 流式 answer 是否为「进子工作流」控制 JSON：
 * {"recordType":"A25","question":"客群洞察"}（可选 end / showFlag）
 */
export function isStreamEnterSubWorkflowControlText(
  text: string,
  options?: WorkflowParseOptions,
  packetQuestion?: unknown,
): boolean {
  return isWorkflowEnterSubPayload(parseWorkflowControlForStream(text, options, packetQuestion))
}

/**
 * 从累积原始文本解析工作流控制 JSON（推荐唯一入口）
 * 合并全文/分片 JSON.parse、正则字段、packet.question，且需满足 complete 才返回
 */
export function parseWorkflowControlForStream(
  parseText: string,
  options?: WorkflowParseOptions,
  packetQuestion?: unknown,
): WorkflowControlPayload | null {
  const text = trimSpacesOnly(parseText)
  if (!text)
    return null

  const fromText = mergePacketEnd(parseWorkflowControlFromText(text), options)
  const fromChunk = options?.answerChunk?.trim()
    ? mergePacketEnd(parseWorkflowControlFromText(options.answerChunk), options)
    : null

  const merged = enrichWorkflowControlWithPacketQuestion(
    mergeWorkflowControlPayload(fromText, fromChunk, extractByRegex(text)),
    packetQuestion,
    text,
  )

  if (merged && isWorkflowControlPayloadComplete(merged))
    return merged

  return null
}

/**
 * 流式快照解析：只信 workflowParseText（原始累积），不用 answer / 历史缓存
 */
export function resolveWorkflowControlFromStreamSnapshot(
  snapshot: {
    workflowParseText?: string
    answer?: string
    workflowAnswerChunk?: string
    workflowDetectedControl?: WorkflowControlPayload | null
  },
  options?: WorkflowParseOptions,
  packetQuestion?: unknown,
): WorkflowControlPayload | null {
  const parseText = trimSpacesOnly(snapshot.workflowParseText ?? '')
  if (parseText) {
    const parsed = parseWorkflowControlForStream(parseText, options, packetQuestion)
    if (parsed)
      return parsed
  }

  const chunkText = trimSpacesOnly(snapshot.workflowAnswerChunk ?? snapshot.answer ?? '')
  if (chunkText && chunkText !== parseText) {
    const fromChunk = parseWorkflowControlForStream(chunkText, options, packetQuestion)
    if (fromChunk)
      return fromChunk
  }

  return null
}

/** 是否包含工作流表单卡片（card-workflow + form_input，且含 text/select/file 字段） */
export function hasWorkflowFormCards(cardList?: Attachment[]): boolean {
  return Array.isArray(cardList) && cardList.some(isWorkflowFormAttachment)
}

/** 是否为「进入子工作流」类控制 JSON（解析后含 recordType + question，且非 showFlag 隐藏路由） */
export function isEnterSubWorkflowControlAnswer(text: string, options?: WorkflowParseOptions): boolean {
  return isWorkflowEnterSubPayload(resolveWorkflowControlFromStreamAnswer(
    { workflowParseText: text, answer: text },
    options,
  ))
}

/** 从混合正文中移除已识别的工作流控制 JSON，保留其余展示内容 */
export function stripWorkflowControlJsonFromText(text: string, options?: WorkflowParseOptions): string {
  const trimmed = text.trim()
  if (!trimmed)
    return ''

  let result = text
  for (const segment of splitTopLevelJsonObjects(trimmed)) {
    const parsed = parseJsonLevel1(segment)
      || parseJsonLevel1(normalizeEscaped(segment))
      || extractByRegex(segment)
    const merged = mergePacketEnd(parsed, options)
    if (!merged)
      continue
    if (isHiddenWorkflowRedirectPayload(merged) || isWorkflowEnterSubPayload(merged))
      result = result.split(segment).join('')
  }
  return trimSpacesOnly(result)
}

/** 工作流控制 JSON 不在气泡中展示正文，仅保留卡片等内容 */
export function resolveDisplayAnswerText(
  text: string,
  options?: WorkflowParseOptions,
  packetQuestion?: unknown,
): string {
  if (!text?.trim())
    return text || ''

  const parsed = parseWorkflowControlForStream(text, options, packetQuestion)
  if (isHiddenWorkflowRedirectPayload(parsed))
    return ''
  if (isWorkflowEnterSubPayload(parsed))
    return stripWorkflowControlJsonFromText(text, options)
  return text
}

/** 将指定 AI 记录标记为工作流表单已提交 */
export function markWorkflowFormSubmittedOnRecord(items: ChatRecord[], sourceKey: string): ChatRecord[] {
  return items.map((item) => {
    if (item.role !== 'ai' || !item.answerList?.[0])
      return item
    const answer = item.answerList[0]
    const key = answer.recordId || item.groupId
    if (key && String(key) === String(sourceKey)) {
      return {
        ...item,
        answerList: [{ ...answer, workflowFormSubmitted: true }],
      }
    }
    return item
  })
}

export function findLastAiRecord(items: ChatRecord[]): { record: ChatRecord, answer: Answer } | null {
  for (let i = items.length - 1; i >= 0; i--) {
    const item = items[i]
    if (item.role === 'ai' && item.answerList?.[0])
      return { record: item, answer: item.answerList[0] }
  }
  return null
}

/**
 * 从 query_user_qa_record_list 返回列表的「最后一条」中提取 workflow 上下文。
 * 优先在最后一条记录的 answerList 中从后往前找到同时带有 recordType 与 workFlowSessionId 的答案。
 */
/** 拉 QA 后最后一条是否已具备 recordType + workFlowSessionId */
export function hasQaWorkflowContext(qaRecords: OriginalRecord[] | null | undefined): boolean {
  const ctx = getLastQaRecordWorkflowContext(qaRecords)
  return Boolean(ctx.recordType && ctx.workFlowSessionId)
}

export function getLastQaRecordWorkflowContext(qaRecords: OriginalRecord[] | null | undefined): {
  recordType?: string
  workFlowSessionId?: string
  busiType?: string
} {
  if (!Array.isArray(qaRecords) || qaRecords.length === 0)
    return {}
  const lastRecord = qaRecords[qaRecords.length - 1]
  const list = lastRecord?.answerList
  if (!Array.isArray(list) || list.length === 0)
    return {}

  for (let i = list.length - 1; i >= 0; i--) {
    const a = list[i] as Answer
    const recordType = a?.recordType != null ? String(a.recordType).trim() : ''
    const workFlowSessionId = resolveWorkFlowSessionId(a) ?? ''
    if (recordType && workFlowSessionId) {
      const busiType = a?.busiType != null ? String(a.busiType).trim() : ''
      return {
        recordType,
        workFlowSessionId,
        ...(busiType ? { busiType } : {}),
      }
    }
  }
  return {}
}

export function processApiResponse(data: OriginalRecord[]): ChatRecord[] {
  const chatRecord: ChatRecord[] = []
  if (data.length === 0)
    return chatRecord
  data.forEach((record) => {
    let questionText = record.question?.trim()
    if (!questionText && Array.isArray(record.answerList)) {
      for (let i = record.answerList.length - 1; i >= 0; i--) {
        const a = record.answerList[i] as Answer
        const fromAnswer = a?.question?.trim() || a?.extra?.optimizedQuestion?.trim()
        if (fromAnswer) {
          questionText = fromAnswer
          break
        }
      }
    }

    // 如果没有 question 字段（后端未返回问题文案），则不生成用户问题气泡，避免展示“空气炮”
    if (questionText) {
      chatRecord.push({
        role: 'user',
        ...record,
        question: questionText,
      })
    }

    if (record.answerList && record.answerList.length > 0) {
      record.answerList.forEach((answer) => {
        answer.isShow = true
        if (answer.answer)
          answer.answer = resolveDisplayAnswerText(answer.answer)
      })
      chatRecord.push({
        role: 'ai',
        answerList: record.answerList,
        question: questionText,
        toolId: record.toolId,
      })
    }
  })

  return chatRecord
}
