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
}

/** L3：正则提取 recordType / question / end / showFlag */
function extractByRegex(text: string): WorkflowControlPayload | null {
  const recordTypeMatch = text.match(/"recordType"\s*:\s*"((?:\\.|[^"\\])*)"/)
  const questionMatch = text.match(/"question"\s*:\s*"((?:\\.|[^"\\])*)"/)
  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 && !questionMatch && !endMatch)
    return null

  const payload: WorkflowControlPayload = {}
  if (recordTypeMatch?.[1])
    payload.recordType = unescapeJsonStringValue(recordTypeMatch[1]).trim()
  if (questionMatch?.[1])
    payload.question = unescapeJsonStringValue(questionMatch[1])
  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
}

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)
  for (let i = candidates.length - 1; i >= 0; i--) {
    const candidate = candidates[i]
    const level1 = parseJsonLevel1(candidate)
    if (level1)
      return level1
    const level2 = parseJsonLevel1(normalizeEscaped(candidate))
    if (level2)
      return level2
    const level3 = extractByRegex(candidate)
    if (level3)
      return level3
  }
  return null
}

/** 流式分包元数据是否携带工作流结束标识 */
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
}

/** 是否包含工作流表单卡片（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 {
  const parsed = tryParseWorkflowControlJson(text, options)
  if (isHiddenWorkflowRedirectPayload(parsed))
    return false
  return Boolean(
    parsed?.recordType != null
    && String(parsed.recordType).trim()
    && parsed.question != null,
  )
}

/** 工作流控制 JSON 不在气泡中展示正文，仅保留卡片等内容 */
export function resolveDisplayAnswerText(text: string, options?: WorkflowParseOptions): string {
  if (!text?.trim())
    return text || ''
  if (isHiddenWorkflowRedirectControl(text, options) || isEnterSubWorkflowControlAnswer(text, options))
    return ''
  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 = a?.workFlowSessionId != null ? String(a.workFlowSessionId).trim() : ''
    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
}
