/**
 * 研究记录 API 模块
 * 封装 /api/ai/v1/research/records 下的所有接口。
 * 前后端 snake_case ↔ camelCase 转换在此处完成。
 */
import request from './request'
import { normalizeResearchPlan } from './research'
import type {
  ResearchRecordSummary,
  ResearchRecordDetail,
  ResearchRecordRunInfo,
  ResearchPlan,
  ResearchTaskDraft,
  ResearchMode,
  ResearchOutputTemplate,
} from '@/types/research'
import type { BasketItem } from '@/types/basket'
import type { ResearchReference } from '@/types/document'

const BASE = '/api/ai/v1/research/records'

// ---------------------------------------------------------------------------
// Normalizers (snake_case → camelCase)
// ---------------------------------------------------------------------------

function normalizeRecordSummary(raw: Record<string, unknown>): ResearchRecordSummary {
  return {
    id: raw.id as string,
    title: raw.title as string,
    mode: (raw.mode as ResearchMode) || 'deep',
    status: (raw.status as ResearchRecordSummary['status']) || 'draft',
    outputTemplate: (raw.output_template as ResearchOutputTemplate) || 'comprehensive',
    summary: (raw.summary as string) || null,
    archived: Boolean(raw.archived),
    versionNo: (raw.version_no as number) || 1,
    createdAt: String(raw.created_at || ''),
    updatedAt: String(raw.updated_at || ''),
  }
}

function normalizeReference(raw: Record<string, unknown>): ResearchReference {
  return {
    doc_id: raw.doc_id as string,
    title: raw.title as string,
    doc_number: (raw.doc_number as string) || undefined,
    relevance_score: (raw.relevance_score as number) || undefined,
    source_group: (raw.source_group as string) || undefined,
    source_label: (raw.source_label as string) || undefined,
    summary: (raw.summary as string) || undefined,
    profile_id: (raw.profile_id as string) || undefined,
    matched_fields: (raw.matched_fields as string[]) || undefined,
    matched_field_labels: (raw.matched_field_labels as string[]) || undefined,
    matched_chunks: Array.isArray(raw.matched_chunks) ? (raw.matched_chunks as ResearchReference['matched_chunks']) : undefined,
  }
}

function normalizeRecordDetail(raw: Record<string, unknown>): ResearchRecordDetail {
  const task = raw.task as Record<string, unknown> | null
  const plan = raw.plan as Record<string, unknown> | null
  const refs = raw.references as Record<string, unknown>[] | null
  return {
    id: raw.id as string,
    title: raw.title as string,
    mode: (raw.mode as ResearchMode) || 'deep',
    status: (raw.status as ResearchRecordDetail['status']) || 'draft',
    outputTemplate: (raw.output_template as ResearchOutputTemplate) || 'comprehensive',
    summary: (raw.summary as string) || null,
    archived: Boolean(raw.archived),
    sessionId: (raw.session_id as string) || null,
    task: task
      ? {
          mode: (task.mode as ResearchMode) || 'deep',
          topic: (task.topic as string) || '',
          question: (task.question as string) || '',
          goal: (task.goal as string) || '',
          timeRange: (task.time_range as string) || '',
          regionScope: (task.region_scope as string) || '',
          orgScope: (task.org_scope as string) || '',
          outputTemplate: (task.output_template as ResearchOutputTemplate) || 'comprehensive',
          depthLevel: (task.depth_level as ResearchTaskDraft['depthLevel']) || 'deep',
          requiredDocIds: (task.required_doc_ids as string[]) || [],
          requiredMatterIds: (task.required_matter_ids as string[]) || [],
        }
      : null,
    plan: plan ? normalizeResearchPlan(plan) : null,
    references: refs ? refs.map((r) => normalizeReference(r)) : null,
    importedItems: (raw.imported_items as BasketItem[]) || null,
    notes: (raw.notes as string) || null,
    parentRecordId: (raw.parent_record_id as string) || null,
    rootRecordId: (raw.root_record_id as string) || null,
    versionNo: (raw.version_no as number) || 1,
    lastError: (raw.last_error as string) || null,
    createdAt: String(raw.created_at || ''),
    updatedAt: String(raw.updated_at || ''),
    completedAt: raw.completed_at ? String(raw.completed_at) : null,
    latestRunId: (raw.latest_run_id as string) || null,
    latestRunStatus: (raw.latest_run_status as string) || null,
    latestRunType: (raw.latest_run_type as string) || null,
    latestRunStartedAt: raw.latest_run_started_at ? String(raw.latest_run_started_at) : null,
    clarificationMessages: (raw.clarification_messages as Record<string, unknown>[]) || null,
    chatMessages: (raw.chat_messages as Record<string, unknown>[]) || null,
  }
}

// ---------------------------------------------------------------------------
// API functions
// ---------------------------------------------------------------------------

export async function fetchRecordList(params: {
  q?: string
  status?: string
  mode?: string
  output_template?: string
  created_after?: string
  created_before?: string
  archived?: boolean
  sort_by?: string
  sort_order?: string
  limit?: number
  offset?: number
}): Promise<{ records: ResearchRecordSummary[]; total: number }> {
  const res = await request.get<{ records: Record<string, unknown>[]; total: number }>(BASE, {
    params: {
      q: params.q || undefined,
      status: params.status || undefined,
      mode: params.mode || undefined,
      output_template: params.output_template || undefined,
      created_after: params.created_after || undefined,
      created_before: params.created_before || undefined,
      archived: params.archived ? true : undefined,
      sort_by: params.sort_by || undefined,
      sort_order: params.sort_order || undefined,
      limit: params.limit || 50,
      offset: params.offset || 0,
    },
  })
  return {
    records: (res.data.records || []).map(normalizeRecordSummary),
    total: res.data.total || 0,
  }
}

export async function fetchRecordDetail(recordId: string): Promise<ResearchRecordDetail> {
  const res = await request.get<Record<string, unknown>>(`${BASE}/${recordId}`)
  return normalizeRecordDetail(res.data)
}

export async function fetchRecordReport(
  recordId: string,
): Promise<{ report: Record<string, unknown> | null; finalDocumentMd: string; updatedAt: string | null }> {
  const res = await request.get<Record<string, unknown>>(`${BASE}/${recordId}/report`)
  return {
    report: (res.data.report as Record<string, unknown>) || null,
    finalDocumentMd: (res.data.final_document_md as string) || '',
    updatedAt: res.data.updated_at ? String(res.data.updated_at) : null,
  }
}

export async function createRecord(data: {
  title: string
  mode: string
  outputTemplate: string
  task: ResearchTaskDraft
  importedItems?: BasketItem[]
  seedDocIds?: string[]
}): Promise<string> {
  const res = await request.post<{ record_id: string }>(BASE, {
    title: data.title,
    mode: data.mode,
    output_template: data.outputTemplate,
    task: {
      mode: data.task.mode,
      topic: data.task.topic,
      question: data.task.question,
      goal: data.task.goal || undefined,
      time_range: data.task.timeRange || undefined,
      region_scope: data.task.regionScope || undefined,
      org_scope: data.task.orgScope || undefined,
      output_template: data.task.outputTemplate,
      depth_level: data.task.depthLevel,
      required_doc_ids: data.task.requiredDocIds || [],
      required_matter_ids: data.task.requiredMatterIds || [],
    },
    imported_items: data.importedItems || undefined,
    seed_doc_ids: data.seedDocIds || undefined,
  })
  return res.data.record_id
}

/** C15: front-end always sends empty object {} */
export async function generateRecordPlan(recordId: string): Promise<ResearchPlan> {
  const res = await request.post<{ plan: Record<string, unknown> }>(`${BASE}/${recordId}/plan`, {})
  return normalizeResearchPlan(res.data.plan)
}

export async function startRecordRun(recordId: string): Promise<ResearchRecordRunInfo> {
  const res = await request.post<Record<string, unknown>>(`${BASE}/${recordId}/run`)
  return {
    runId: res.data.run_id as string,
    recordId: res.data.record_id as string,
    status: (res.data.status as string) || 'pending',
  }
}

export async function startRecordSectionRerun(
  recordId: string,
  body: { section_title: string; section_summary?: string; source_doc_ids?: string[] },
): Promise<ResearchRecordRunInfo> {
  const res = await request.post<Record<string, unknown>>(`${BASE}/${recordId}/sections/rerun`, body)
  return {
    runId: res.data.run_id as string,
    recordId: res.data.record_id as string,
    status: (res.data.status as string) || 'pending',
  }
}

/** Build SSE subscription URL for useSSE.connect() */
export function buildRunEventsUrl(recordId: string, runId: string): string {
  return `${BASE}/${recordId}/runs/${runId}/events`
}

export async function archiveRecord(recordId: string): Promise<void> {
  await request.post(`${BASE}/${recordId}/archive`)
}

export async function deleteRecord(recordId: string): Promise<void> {
  await request.delete(`${BASE}/${recordId}`)
}

/** F3: Create a new version from an existing record */
export async function regenerateRecord(
  recordId: string,
  task: Record<string, unknown>,
): Promise<{ id: string; title: string; versionNo: number }> {
  const res = await request.post<Record<string, unknown>>(`${BASE}/${recordId}/regenerate`, {
    task,
  })
  return {
    id: res.data.id as string,
    title: res.data.title as string,
    versionNo: (res.data.version_no as number) || 1,
  }
}

/** F4: Fetch all versions of a record */
export async function fetchVersions(recordId: string): Promise<ResearchRecordSummary[]> {
  const res = await request.get<Record<string, unknown>[]>(`${BASE}/${recordId}/versions`)
  return (res.data || []).map(normalizeRecordSummary)
}

/** F6: Import items to an existing record */
export async function importItemsToRecord(
  recordId: string,
  items: Record<string, unknown>[],
): Promise<{ status: string; totalItems: number; needsReplan: boolean }> {
  const res = await request.post<Record<string, unknown>>(`${BASE}/${recordId}/import-items`, { items })
  return {
    status: res.data.status as string,
    totalItems: (res.data.total_items as number) || 0,
    needsReplan: Boolean(res.data.needs_replan),
  }
}

/** F1: Build clarify SSE URL (direct stream, not run-based) */
export function buildClarifyUrl(recordId: string): string {
  return `${BASE}/${recordId}/clarify`
}

/** F2: Build chat SSE URL (direct stream) */
export function buildChatUrl(recordId: string): string {
  return `${BASE}/${recordId}/chat`
}

/** Save user-edited research plan (PUT /{id}/plan) */
export async function updateRecordPlan(recordId: string, plan: ResearchPlan): Promise<void> {
  await request.put(`${BASE}/${recordId}/plan`, {
    summary: plan.summary,
    objectives: plan.objectives,
    sub_questions: plan.subQuestions,
    retrieval_focus: plan.retrievalFocus,
    section_outline: plan.sectionOutline,
    expected_deliverables: plan.expectedDeliverables,
    notes: plan.notes,
    included_doc_ids: plan.includedDocIds,
    included_matter_ids: plan.includedMatterIds,
  })
}

/** Upload a file to extract text as research material */
export async function uploadMaterial(
  recordId: string,
  file: File,
): Promise<{
  fileId: string
  originalFilename: string
  fileType: string
  charCount: number
  extractedTextPreview: string
}> {
  const formData = new FormData()
  formData.append('file', file)
  const res = await request.post<Record<string, unknown>>(
    `${BASE}/${recordId}/upload-material`,
    formData,
    // Must unset the axios instance default 'application/json' so the browser
    // can set 'multipart/form-data; boundary=...' automatically from FormData.
    { headers: { 'Content-Type': undefined } },
  )
  const d = res.data as Record<string, unknown>
  return {
    fileId: d.file_id as string,
    originalFilename: d.original_filename as string,
    fileType: d.file_type as string,
    charCount: (d.char_count as number) || 0,
    extractedTextPreview: (d.extracted_text_preview as string) || '',
  }
}

/**
 * Download an uploaded file via authenticated request, then trigger browser save.
 */
export async function downloadUploadedFile(recordId: string, fileId: string, filename: string): Promise<void> {
  const res = await request.get(`${BASE}/${recordId}/uploaded-files/${fileId}`, {
    responseType: 'blob',
  })
  const blob = new Blob([res.data as BlobPart])
  const url = URL.createObjectURL(blob)
  const a = document.createElement('a')
  a.href = url
  a.download = filename
  document.body.appendChild(a)
  a.click()
  document.body.removeChild(a)
  URL.revokeObjectURL(url)
}
