import type { DataProcessConfig, DataProcessDatasetSplit } from '@/types/dataProcess'
import type {
  GenerationControlOptions,
  PreprocessOption,
  StructuredProcessOptions,
  UnstructuredPreprocessOption,
  UnstructuredProcessOptions,
  ProcessType,
} from './types'
import { normalizeQaPairsGenerationCount } from './types'

const LEGACY_DEFAULT_GENERATION_PROMPT = '你是一名专业的数据生成助手。请根据输入内容生成准确、完整、可直接用于模型训练的问答数据。仅输出符合所选输出类型和目标格式的内容，答案应事实清晰、语言自然，不要添加无关说明。'

const PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT = '你是一名专业的数据生成助手。请严格依据输入内容生成准确、完整、可直接用于监督微调的问答数据。只生成问题和最终答案，不输出分析、推理过程或来源中不存在的信息；答案应事实清晰、语言自然。'

const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT = '你是一名专业的推理数据生成助手。请严格依据输入内容生成问题、可核验的推理过程和最终答案。推理需要说明关键依据与必要步骤，不得引入来源中不存在的事实；最终答案应准确、完整且语言自然。'

export const PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_2 = `你是一名专业的数据生成助手。请根据下方来源内容生成可用于监督微调的问答数据。
来源内容：
{{ content }}
要求：严格依据来源生成问题和最终答案，不输出分析、中间思考过程或来源中不存在的信息；答案应准确、完整、语言自然。`

export const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_2 = `你是一名专业的推理数据生成助手。请根据下方来源内容生成可用于推理模型训练的问答数据。
来源内容：
{{ content }}
要求：每条数据必须包含问题、可核验的中间思考过程和最终答案。中间思考过程需说明关键依据、必要步骤和中间计算，不得跳过推理只给结论，也不得引入来源中不存在的事实。`

export const PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_3 = `你是一名专业的数据生成专家。你的任务是根据下方的来源内容，提取核心信息并生成高质量的问答数据，用于大模型的监督微调（SFT）。

来源内容：
{{ content }}

任务要求：
1. 问题构建：基于来源内容设计问题，问题应当具有实际意义，不仅限于表面的事实问答，可以包括对内容的主旨概括、具体细节的询问或应用场景的探讨。
2. 答案生成：严格依据来源内容生成最终答案，答案需准确无误、逻辑连贯、完整且表达自然流畅。
3. 忠于原文：不输出分析、中间思考过程或任何多余的解释，切勿引入来源内容中未提及的事实或主观臆造的信息。`

export const PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_3 = `你是一名具有深厚专业知识的推理数据生成专家。你的任务是根据给定的来源内容，生成高质量、逻辑严密且可用于训练推理大语言模型（如 O1 或 DeepSeek-R1）的问答数据。

来源内容：
{{ content }}

任务要求：
1. 多维度提问：从来源内容中提取核心概念或关键逻辑，构建具有挑战性的问题，避免简单的信息抽取，提问可以涉及因果分析、方案设计、逻辑推断或跨段落信息整合。
2. 深度思维链（CoT）：
   - 必须提供详尽的中间思考过程。
   - 在思考过程中，需首先分析问题的核心需求，然后逐步拆解来源内容中的相关事实或数据。
   - 对于涉及计算或多步推导的问题，必须一步步展示计算或推导过程，并说明每一步的理论依据或事实来源。
   - 如果遇到矛盾或缺失的信息，需在思考过程中指出，并合理假设或推理，但最终答案不得编造来源中完全不相关的事实。
3. 最终答案：在充分推理后给出明确的最终结论。结论应当是对问题的直接回应，语言自然、结构清晰，并且结论必须能从前面的思考过程中自然得出。

数据格式要求：
请确保每条生成的数据清晰包含“问题”、“中间思考过程”和“最终答案”三个部分，且内容充实、专业。`

export const DEFAULT_STANDARD_GENERATION_PROMPT = `你是一名专业的数据生成专家。请基于下方来源内容，生成高质量、可直接用于大模型监督微调（SFT）的问答数据。

来源内容：
{{ content }}

任务要求：
1. 提问有价值：问题应聚焦来源内容的核心信息点，避免无意义的字面抄录或过于宽泛的提问；优先设计需要理解、归纳或推理才能回答的问题，例如主旨概括、细节追问、因果解释或场景应用。
2. 答案忠于来源：严格依据来源内容作答，不得引入来源中不存在的事实、观点或数据；若来源信息不足以完整回答该问题，请如实说明不足，不要编造或臆测。
3. 答案完整且自然：回答应直接回应问题，要点完整、逻辑清晰、语言流畅；涉及多项内容时建议分点陈述，但不必照搬原文措辞。`

export const DEFAULT_REASONING_GENERATION_PROMPT = `你是一名具有深厚专业知识的推理数据生成专家。请基于下方来源内容，生成包含完整思维链、可用于训练推理大语言模型（如 O1、DeepSeek-R1）的问答数据。

来源内容：
{{ content }}

任务要求：
1. 提问有挑战性：从来源内容中提炼需要多步思考才能回答的问题，避免可直接定位答案的简单事实抽取；可涉及因果分析、逻辑推断或跨信息整合。
2. 推理可核验：逐步展开思考过程，每个关键结论都应标注其在来源中的依据或必要的中间推导；不得跳过关键步骤只给结论，也不得引入来源中完全不存在的事实。
3. 信息不足时如实处理：遇到来源信息缺失或相互矛盾时，需在推理中明确指出，并基于来源做出合理假设，但不得编造与来源无关的内容。
4. 最终答案须能从前面的推理过程中自然得出，语言简洁、结构清晰。`

export function defaultGenerationPrompt(outputType: 'standard' | 'reasoning') {
  return outputType === 'reasoning'
    ? DEFAULT_REASONING_GENERATION_PROMPT
    : DEFAULT_STANDARD_GENERATION_PROMPT
}

export function isBuiltInGenerationPrompt(value: string) {
  const normalize = (s: string) => s.replace(/\r\n/g, '\n').trim()
  const normalizedValue = normalize(value)
  return [
    LEGACY_DEFAULT_GENERATION_PROMPT,
    PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT,
    PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_2,
    PREVIOUS_DEFAULT_STANDARD_GENERATION_PROMPT_3,
    PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT,
    PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_2,
    PREVIOUS_DEFAULT_REASONING_GENERATION_PROMPT_3,
    DEFAULT_STANDARD_GENERATION_PROMPT,
    DEFAULT_REASONING_GENERATION_PROMPT,
  ].map(normalize).includes(normalizedValue)
}

export function createDefaultStructuredOptions(): StructuredProcessOptions {
  return {
    preprocessOptions: ['clean_invalid', 'detect_structure', 'deduplicate', 'normalize_format'],
    semanticEnrichment: false,
    qaPairsPerRow: 1,
    datasetSplit: { train: 80, validation: 10, test: 10 },
    generationModelId: '',
    generationPrompt: DEFAULT_STANDARD_GENERATION_PROMPT,
    outputType: 'standard',
    reasoningDetail: 'normal',
    temperature: 0.7,
    maxTokens: 1024,
    jsonMode: false,
    qualityFilterEnabled: false,
    filterLowQuality: true,
    filterShortContent: true,
    minOutputLength: 20,
  }
}

export function createDefaultUnstructuredOptions(): UnstructuredProcessOptions {
  return {
    preprocessOptions: [
      'clean_invalid_content',
      'detect_document_structure',
      'merge_short_content',
      'filter_low_quality',
      'deduplicate_content',
      'preserve_context',
    ],
    chunkMethod: 'layout_hybrid',
    chunkSize: 800,
    chunkOverlap: 100,
    minChunkSize: 100,
    semanticBreakpointPercentile: 95,
    preserveTables: true,
    preserveCodeBlocks: true,
    preserveLists: true,
    semanticEnrichment: false,
    qaPairsPerChunk: 1,
    datasetSplit: { train: 80, validation: 10, test: 10 },
    generationModelId: '',
    generationPrompt: DEFAULT_STANDARD_GENERATION_PROMPT,
    outputType: 'standard',
    reasoningDetail: 'normal',
    temperature: 0.7,
    maxTokens: 1024,
    jsonMode: false,
    qualityFilterEnabled: false,
    filterLowQuality: true,
    filterShortContent: true,
    minOutputLength: 20,
  }
}

function configValue<T>(config: DataProcessConfig, key: string, fallback: T): T {
  return Object.prototype.hasOwnProperty.call(config, key) ? config[key] as T : fallback
}

function numberValue(config: DataProcessConfig, key: string, fallback: number): number {
  const value = Number(configValue(config, key, fallback))
  return Number.isFinite(value) ? value : fallback
}

function datasetSplitValue(config: DataProcessConfig, fallback: DataProcessDatasetSplit) {
  const value = config.dataset_split
  if (!value || typeof value !== 'object') return { ...fallback }
  const split = value as unknown as Record<string, unknown>
  const splitNumber = (key: keyof DataProcessDatasetSplit) => {
    const parsed = Number(split[key])
    return Number.isFinite(parsed) ? parsed : fallback[key]
  }
  return {
    train: splitNumber('train'),
    validation: splitNumber('validation'),
    test: splitNumber('test'),
  }
}

function generationOptionsFromConfig(
  config: DataProcessConfig,
  defaults: GenerationControlOptions,
): GenerationControlOptions {
  const outputType = configValue(config, 'output_type', defaults.outputType) === 'reasoning'
    ? 'reasoning'
    : 'standard'
  const configuredPrompt = String(
    configValue(config, 'generation_prompt', defaults.generationPrompt),
  )
  return {
    generationModelId: configValue(config, 'generation_model_id', defaults.generationModelId),
    generationPrompt: isBuiltInGenerationPrompt(configuredPrompt)
      ? defaultGenerationPrompt(outputType)
      : configuredPrompt,
    outputType,
    reasoningDetail: configValue(config, 'reasoning_detail', defaults.reasoningDetail) === 'detailed'
      ? 'detailed'
      : 'normal',
    temperature: numberValue(config, 'temperature', defaults.temperature),
    maxTokens: numberValue(config, 'max_tokens', defaults.maxTokens),
    jsonMode: Boolean(configValue(config, 'json_mode', defaults.jsonMode)),
    qualityFilterEnabled: Boolean(configValue(
      config,
      'quality_filter_enabled',
      defaults.qualityFilterEnabled,
    )),
    filterLowQuality: Boolean(configValue(
      config,
      'filter_low_quality',
      defaults.filterLowQuality,
    )),
    filterShortContent: Boolean(configValue(
      config,
      'filter_short_content',
      defaults.filterShortContent,
    )),
    minOutputLength: numberValue(config, 'min_output_length', defaults.minOutputLength),
  }
}

export function createStructuredOptionsFromConfig(config: DataProcessConfig): StructuredProcessOptions {
  const defaults = createDefaultStructuredOptions()
  const preprocessOptions = configValue<unknown>(config, 'preprocess_options', [])
  return {
    ...defaults,
    ...generationOptionsFromConfig(config, defaults),
    preprocessOptions: Array.isArray(preprocessOptions)
      ? preprocessOptions.map(String) as PreprocessOption[]
      : defaults.preprocessOptions,
    semanticEnrichment: Boolean(configValue(
      config,
      'semantic_enrichment',
      defaults.semanticEnrichment,
    )),
    qaPairsPerRow: normalizeQaPairsGenerationCount(
      numberValue(config, 'qa_pairs_per_row', defaults.qaPairsPerRow),
      defaults.qaPairsPerRow,
    ),
    datasetSplit: datasetSplitValue(config, defaults.datasetSplit),
  }
}

export function createUnstructuredOptionsFromConfig(config: DataProcessConfig): UnstructuredProcessOptions {
  const defaults = createDefaultUnstructuredOptions()
  const preprocessOptions = configValue<unknown>(config, 'preprocess_options', [])
  return {
    ...defaults,
    ...generationOptionsFromConfig(config, defaults),
    preprocessOptions: Array.isArray(preprocessOptions)
      ? preprocessOptions.map(String) as UnstructuredPreprocessOption[]
      : defaults.preprocessOptions,
    chunkMethod: configValue(config, 'chunk_method', defaults.chunkMethod),
    chunkSize: numberValue(config, 'chunk_size', defaults.chunkSize),
    chunkOverlap: numberValue(config, 'chunk_overlap', defaults.chunkOverlap),
    minChunkSize: numberValue(config, 'min_chunk_size', defaults.minChunkSize),
    semanticBreakpointPercentile: numberValue(
      config,
      'semantic_breakpoint_percentile',
      defaults.semanticBreakpointPercentile,
    ),
    preserveTables: Boolean(configValue(config, 'preserve_tables', defaults.preserveTables)),
    preserveCodeBlocks: Boolean(configValue(
      config,
      'preserve_code_blocks',
      defaults.preserveCodeBlocks,
    )),
    preserveLists: Boolean(configValue(config, 'preserve_lists', defaults.preserveLists)),
    semanticEnrichment: Boolean(configValue(
      config,
      'semantic_enrichment',
      defaults.semanticEnrichment,
    )),
    qaPairsPerChunk: normalizeQaPairsGenerationCount(
      numberValue(config, 'qa_pairs_per_chunk', defaults.qaPairsPerChunk),
      defaults.qaPairsPerChunk,
    ),
    datasetSplit: datasetSplitValue(config, defaults.datasetSplit),
  }
}

export function previewAffectingOptionsFor(
  processType: ProcessType,
  structured: StructuredProcessOptions,
  unstructured: UnstructuredProcessOptions,
) {
  if (processType === 'structured') return { preprocessOptions: structured.preprocessOptions }
  if (processType !== 'unstructured') return null
  const {
    preprocessOptions,
    chunkMethod,
    chunkSize,
    chunkOverlap,
    minChunkSize,
    semanticBreakpointPercentile,
    preserveTables,
    preserveCodeBlocks,
    preserveLists,
  } = unstructured
  return {
    preprocessOptions,
    chunkMethod,
    chunkSize,
    chunkOverlap,
    minChunkSize,
    semanticBreakpointPercentile,
    preserveTables,
    preserveCodeBlocks,
    preserveLists,
  }
}

export function generationAffectingOptionsFor(
  processType: ProcessType,
  structured: StructuredProcessOptions,
  unstructured: UnstructuredProcessOptions,
) {
  const options = processType === 'unstructured' ? unstructured : structured
  if (processType === 'external') return null
  const common = {
    semanticEnrichment: options.semanticEnrichment,
    datasetSplit: options.datasetSplit,
    generationModelId: options.generationModelId,
    generationPrompt: options.generationPrompt,
    outputType: options.outputType,
    reasoningDetail: options.reasoningDetail,
    temperature: options.temperature,
    maxTokens: options.maxTokens,
    jsonMode: options.jsonMode,
    qualityFilterEnabled: options.qualityFilterEnabled,
    filterLowQuality: options.filterLowQuality,
    filterShortContent: options.filterShortContent,
    minOutputLength: options.minOutputLength,
  }
  return processType === 'unstructured'
    ? { ...common, qaPairsPerChunk: unstructured.qaPairsPerChunk }
    : { ...common, qaPairsPerRow: structured.qaPairsPerRow }
}
