import React from 'react'
import { cn } from '../../../utils/cn'
import { getDesktopActiveProjectId } from '../../../desktop/activeProject'
import { deriveGenerationModelCatalogStatus, findModelOptionByIdentifier, useGenerationModelOptionsState } from '../adapters/modelOptionsAdapter'
import { type ModelParameterControl } from '../../../config/modelCatalogMeta'
import type { GenerationCanvasNode } from '../model/generationCanvasTypes'
import { getGenerationNodeExecutionKind, isImageLikeGenerationNodeKind, isVideoLikeGenerationNodeKind } from '../model/generationNodeKinds'
import { useGenerationCanvasStore } from '../store/generationCanvasStore'
import { importWorkbenchLocalAssetFile } from '../../api/assetUploadApi'
import {
  type DynamicCatalogControl,
  type ImageUrlSlot,
  assetUrl,
  buildEffectiveImageCatalogConfig,
  buildImageUrlSlots,
  buildModelControls,
  defaultPatchForCatalogControl,
  defaultPatchForControls,
  edgeModeForGroup,
  getEdgeSourceForSlot,
  getSlotNodeRef,
  getSlotThumbUrl,
  imageCatalogReferenceSlot,
  parseControlInput,
  readMeta,
  removePreviousControlParams,
  resultPreviewUrl,
} from './controls/parameterControlModel'
import {
  type ArchetypeArraySlot,
  appendArchetypeArrayValue,
  applyArchetypeModeSwitch,
  archetypeModeArraySlots,
  archetypeModeChoices,
  archetypeModeSlots,
  archetypeModeSourceVideoSlot,
  currentArchetypeMode,
  readArchetypeArray,
} from './controls/archetypeMeta'
import ModeBar from './controls/ModeBar'
import AssetReference, { type AssetSlot } from '../../assets/AssetReference'
import type { AssetRef } from '../../assets/assetTypes'
import { moveArrayItem } from '../../assets/assetTypes'
import { removeMention } from '../../assets/promptMentions'
import { showInfoToast } from '../../../utils/showInfoToast'
import InlineParameterBar from './InlineParameterBar'
import { useNodeModelAutoSelect } from './useNodeModelAutoSelect'
import { resolveArchetypeForOption, resolveRenderedControls } from './nodeModelArchetype'
import { ASPECT_RATIO_KEYS, normalizeAspectRatioToWH } from './aspectRatio'

// 模块级常量：比例参数的 key 白名单（与 aspectRatio.ts 的 ASPECT_RATIO_KEYS 保持一致）。
const ASPECT_RATIO_KEY_SET = new Set<string>(ASPECT_RATIO_KEYS)

type NodeParameterControlsProps = {
  node: GenerationCanvasNode
  section?: 'all' | 'references' | 'parameters' | 'model' | 'controls'
  /** 点参考 tile → 在描述框光标处插入 @ 引用 chip(主路径,由 composer 注入 editor 命令)。 */
  onInsertMention?: (url: string) => void
}


export default function NodeParameterControls({
  node,
  section = 'all',
  onInsertMention,
}: NodeParameterControlsProps): JSX.Element | null {
  const nodes = useGenerationCanvasStore((state) => state.nodes)
  const edges = useGenerationCanvasStore((state) => state.edges)
  const updateNode = useGenerationCanvasStore((state) => state.updateNode)
  const updateEdgeMode = useGenerationCanvasStore((state) => state.updateEdgeMode)
  const storeConnectNodes = useGenerationCanvasStore((state) => state.connectNodes)
  const storeDisconnectEdge = useGenerationCanvasStore((state) => state.disconnectEdge)
  const modelOptionsState = useGenerationModelOptionsState(node.kind)
  const modelOptions = modelOptionsState.options
  const modelCatalogStatus = deriveGenerationModelCatalogStatus(node.kind, modelOptionsState)
  const meta = React.useMemo<Record<string, unknown>>(() => node.meta || {}, [node.meta])
  const [uploadingSlotKey, setUploadingSlotKey] = React.useState('')
  const [uploadError, setUploadError] = React.useState('')
  // 统一的「哪个槽的选择器展开」(单/数组共用一个,P1 归一)+ 数组/源视频上传中标记。
  const [openSlotKey, setOpenSlotKey] = React.useState('')
  const [uploadingArrayKey, setUploadingArrayKey] = React.useState('')
  const isImageLike = isImageLikeGenerationNodeKind(node.kind)
  const isVideoLike = isVideoLikeGenerationNodeKind(node.kind)
  // C5：文本节点也是可生成节点（executionKind:'text'）——要渲染模型选择器，否则没处选模型。
  const isTextLike = getGenerationNodeExecutionKind(node.kind) === 'text'
  const isGenerationNode = isImageLike || isVideoLike || isTextLike

  const selectedModelValue = readMeta(meta, 'modelKey') || readMeta(meta, 'modelAlias') || readMeta(meta, 'imageModel') || readMeta(meta, 'videoModel')
  const selectedModelOption = findModelOptionByIdentifier(modelOptions, selectedModelValue) || null
  // 认得的模型 → 内置档案（供应商无关）；驱动模式分段切换 + 当前模式的槽/参数。认不出 → null（走 flat）。
  const archetype = resolveArchetypeForOption(selectedModelOption)
  const archMode = archetype ? currentArchetypeMode(archetype, meta) : null
  const imageCatalogConfig = archetype ? null : buildEffectiveImageCatalogConfig(selectedModelOption?.meta)
  const renderedControls = resolveRenderedControls(selectedModelOption, meta, isImageLike, isVideoLike)

  const updateMeta = (patch: Record<string, unknown>) => {
    updateNode(node.id, {
      meta: { ...(node.meta || {}), ...patch },
    })
  }

  const handleModelChange = (value: string) => {
    const nextOption = findModelOptionByIdentifier(modelOptions, value)
    const controls = buildModelControls(nextOption?.meta, isImageLike, isVideoLike)
    const defaultPatch = defaultPatchForControls(controls)
    updateNode(node.id, {
      meta: {
        ...removePreviousControlParams(node.meta || {}, renderedControls),
        modelKey: nextOption?.modelKey || nextOption?.value || value || null,
        modelAlias: nextOption?.modelAlias || nextOption?.value || value || null,
        modelVendor: nextOption?.vendor || null,
        vendor: nextOption?.vendor || null,
        modelLabel: nextOption?.label || value || null,
        ...defaultPatch,
        ...(isVideoLike
          ? { videoModel: nextOption?.value || value || null, videoModelVendor: nextOption?.vendor || null }
          : { imageModel: nextOption?.value || value || null, imageModelVendor: nextOption?.vendor || null }),
      },
    })
  }

  useNodeModelAutoSelect({
    node,
    meta,
    modelOptions,
    selectedModelValue,
    selectedModelOption,
    archetype,
    isGenerationNode,
    isImageLike,
    isVideoLike,
    updateNode,
  })

  if (!isGenerationNode) return null

  const handleParameterControlChange = (control: ModelParameterControl, value: string) => {
    const parsed = parseControlInput(control, value)
    const patch: Record<string, unknown> = { [control.key]: parsed }
    // 跨键同步：凡写任意比例 key（aspect_ratio / size / ratio / image_size），
    // 同时把规范化后的 W:H 写进 aspect_ratio（最高优先级读取键）。
    // 防止旧模式遗留的 stale aspect_ratio 遮蔽当前模式的比例选择（如 t2i→改图后 image_size 被旧值盖住）。
    if (ASPECT_RATIO_KEY_SET.has(control.key)) {
      const wh = normalizeAspectRatioToWH(parsed)
      if (wh) patch.aspect_ratio = wh
    }
    updateMeta(patch)
  }

  const handleCatalogControlChange = (control: DynamicCatalogControl, value: string) => {
    updateMeta(defaultPatchForCatalogControl({ ...control, defaultValue: value }))
  }

  // 切生成方式：只改 modeId，参考值全局保留（切回照片还在）；互斥发生在传输投影。
  const handleModeSwitch = (modeId: string) => {
    if (!archetype) return
    updateNode(node.id, { meta: applyArchetypeModeSwitch(node.meta || {}, archetype, modeId) })
    setOpenSlotKey('')
  }

  // ── C3 数组参考槽（全能参考，meta-only）：append / remove / 上传，写 node.meta[metaKey] 数组 ──
  const setArrayValue = (metaKey: string, next: string[]) => updateMeta({ [metaKey]: next })
  const handleArrayAdd = (slot: ArchetypeArraySlot, url: string) => {
    // 单源去重/上限：与拖入/连线共用 appendArchetypeArrayValue（规则 1：不另开写路径）。
    const result = appendArchetypeArrayValue(node.meta || {}, slot, url)
    if (result.status === 'full') { showInfoToast(`最多 ${slot.max} 个${slot.label}`); return } // 到上限:明确告知(对抗评审:别静默丢)
    if (result.status !== 'added') return // empty / duplicate：静默
    setArrayValue(slot.metaKey, result.next)
    setOpenSlotKey('')
  }
  const handleArrayRemove = (metaKey: string, index: number) => {
    const current = readArchetypeArray(node.meta || {}, metaKey)
    const removedUrl = current[index] // 必须在 filter 前取(对抗评审 must-fix:删后数组已变)
    const next = current.filter((_, i) => i !== index)
    // image 数组(= character 参考)删除时,同步抹掉描述框里指向它的 @ chip。
    // meta 删除 + prompt 改写**合并成单个 updateNode**(对抗评审 must-fix:保 undo 原子性 + 一次持久化;
    // 走与现有 meta 删除同一持久化路径,不会出现刷新后 chip 复活)。chip/@ 只服务 image 参考,其余照旧。
    if (metaKey === 'referenceImageUrls' && removedUrl) {
      const nextPrompt = removeMention(node.prompt || '', removedUrl)
      if (nextPrompt !== (node.prompt || '')) {
        updateNode(node.id, { meta: { ...(node.meta || {}), [metaKey]: next }, prompt: nextPrompt })
        return
      }
    }
    setArrayValue(metaKey, next)
  }
  const handleArrayUpload = async (slot: ArchetypeArraySlot, file: File | null | undefined) => {
    if (!file) return
    setUploadingArrayKey(slot.metaKey)
    setUploadError('')
    try {
      const uploaded = await importWorkbenchLocalAssetFile(file, file.name || slot.label, { ownerNodeId: node.id, taskKind: 'image_edit' })
      const url = assetUrl(uploaded)
      if (!url) throw new Error('服务器没有返回素材 URL')
      handleArrayAdd(slot, url)
    } catch (error) {
      setUploadError(error instanceof Error ? error.message : String(error))
    } finally {
      setUploadingArrayKey('')
    }
  }

  // D3 源视频单槽（video-edit）：上传一个视频 → 写 meta.sourceVideoUrl（传输映射成 video_url）。
  const handleSourceVideoUpload = async (metaKey: string, file: File | null | undefined) => {
    if (!file) return
    setUploadingArrayKey(metaKey)
    setUploadError('')
    try {
      const uploaded = await importWorkbenchLocalAssetFile(file, file.name || '源视频', { ownerNodeId: node.id, taskKind: 'image_edit' })
      const url = assetUrl(uploaded)
      if (!url) throw new Error('服务器没有返回视频 URL')
      updateMeta({ [metaKey]: url })
    } catch (error) {
      setUploadError(error instanceof Error ? error.message : String(error))
    } finally {
      setUploadingArrayKey('')
    }
  }
  const handleSlotAssignment = (slot: ImageUrlSlot, newSourceNodeId: string) => {
    const targetMode = edgeModeForGroup(slot.group)
    if (!newSourceNodeId) {
      const existingEdge = edges.find((e) => e.target === node.id && e.mode === targetMode)
      if (existingEdge) storeDisconnectEdge(existingEdge.id)
      const clearPatch: Record<string, unknown> = { [slot.key]: null, [slot.key + '_nodeRef']: null }
      if (slot.group === 'first_frame') { clearPatch.firstFrameUrl = null; clearPatch.firstFrameRef = null }
      if (slot.group === 'last_frame') { clearPatch.lastFrameUrl = null; clearPatch.lastFrameRef = null }
      if (slot.group === 'reference') { clearPatch.referenceImages = []; clearPatch.referenceImageUrl = null; clearPatch.referenceImageRef = null }
      updateNode(node.id, { meta: { ...meta, ...clearPatch } })
      setOpenSlotKey('')
      return
    }
    const existingFromSource = edges.find((e) => e.source === newSourceNodeId && e.target === node.id)
    if (existingFromSource) {
      if (existingFromSource.mode !== targetMode) updateEdgeMode(existingFromSource.id, targetMode)
    } else {
      storeConnectNodes(newSourceNodeId, node.id, targetMode)
    }
    const conflictEdge = edges.find((e) => e.target === node.id && e.mode === targetMode && e.source !== newSourceNodeId)
    if (conflictEdge) storeDisconnectEdge(conflictEdge.id)
    const sourceNode = nodes.find((n) => n.id === newSourceNodeId)
    const url = resultPreviewUrl(sourceNode)
    const patch: Record<string, unknown> = { [slot.key]: url || null, [slot.key + '_nodeRef']: newSourceNodeId }
    if (slot.group === 'first_frame') { patch.firstFrameUrl = url || null; patch.firstFrameRef = newSourceNodeId }
    if (slot.group === 'last_frame') { patch.lastFrameUrl = url || null; patch.lastFrameRef = newSourceNodeId }
    if (slot.group === 'reference') { patch.referenceImages = url ? [url] : []; patch.referenceImageUrl = url || null; patch.referenceImageRef = newSourceNodeId }
    updateNode(node.id, { meta: { ...meta, ...patch } })
    setOpenSlotKey('')
  }
  // 把单帧槽设成一个给定 URL（上传 / 选项目素材共用）：断开该组旧画布边(切到无源节点的 url)、写 flat meta。
  const setSingleFrameUrlMeta = (slot: ImageUrlSlot, url: string) => {
    const targetMode = edgeModeForGroup(slot.group)
    const existingEdge = edges.find((e) => e.target === node.id && e.mode === targetMode)
    if (existingEdge) storeDisconnectEdge(existingEdge.id)
    const latestMeta = useGenerationCanvasStore.getState().nodes.find((n) => n.id === node.id)?.meta || meta
    const patch: Record<string, unknown> = { [slot.key]: url, [slot.key + '_nodeRef']: null }
    if (slot.group === 'first_frame') { patch.firstFrameUrl = url; patch.firstFrameRef = null }
    if (slot.group === 'last_frame') { patch.lastFrameUrl = url; patch.lastFrameRef = null }
    if (slot.group === 'reference') { patch.referenceImages = [url]; patch.referenceImageUrl = url; patch.referenceImageRef = null }
    updateNode(node.id, { meta: { ...latestMeta, ...patch } })
    setOpenSlotKey('')
  }
  const handleSlotUpload = async (slot: ImageUrlSlot, file: File | null | undefined) => {
    if (!file) return
    if (!file.type.startsWith('image/')) {
      setUploadError('只能选择图片文件')
      return
    }
    setUploadingSlotKey(slot.key)
    setUploadError('')
    try {
      const uploaded = await importWorkbenchLocalAssetFile(file, file.name || slot.label, {
        ownerNodeId: node.id,
        taskKind: 'image_edit',
      })
      const url = assetUrl(uploaded)
      if (!url) throw new Error('服务器没有返回图片 URL')
      setSingleFrameUrlMeta(slot, url)
    } catch (error) {
      setUploadError(error instanceof Error ? error.message : String(error))
    } finally {
      setUploadingSlotKey('')
    }
  }

  const modelImageUrlSlots = [
    ...buildImageUrlSlots(selectedModelOption?.meta),
    ...imageCatalogReferenceSlot(imageCatalogConfig),
  ].filter((slot, index, slots) => slots.findIndex((item) => item.key === slot.key && item.group === slot.group) === index)
  // 认得档案 → 槽位严格由当前模式声明（首帧 / 首尾帧…，切模式即换整组，互斥 hide）。
  // 认不出 → 现有启发式槽 + 视频模型 首/尾帧 兜底。
  const imageUrlSlots: ImageUrlSlot[] = archMode
    ? archetypeModeSlots(archMode)
    : isVideoLike && modelImageUrlSlots.length === 0
      ? [
          { key: 'firstFrameUrl', label: '首帧', group: 'first_frame' },
          { key: 'lastFrameUrl', label: '尾帧', group: 'last_frame' },
        ]
      : modelImageUrlSlots
  const modeChoices = archetype ? archetypeModeChoices(archetype) : []
  const showModeBar = modeChoices.length > 1
  // 当前模式的数组参考槽（全能参考，meta-only）+ 源视频单槽（HappyHorse 视频编辑）。
  const arraySlots: ArchetypeArraySlot[] = archMode ? archetypeModeArraySlots(archMode) : []
  const sourceVideoSlot = archMode ? archetypeModeSourceVideoSlot(archMode) : null
  const showReferences = section === 'all' || section === 'references'

  // ── P1 统一参考槽：声明式 AssetSlot 列表 + 当前值 + 三类回调（单帧连边 / 数组 meta / 源视频 meta，复用上面已验证的写入逻辑）──
  const assetSlots: AssetSlot[] = [
    ...imageUrlSlots.map((s): AssetSlot => ({ key: s.key, label: s.label, accept: 'image', form: 'single', persistAsEdge: true, numbered: false, max: 1 })),
    ...arraySlots.map((s): AssetSlot => ({ key: s.metaKey, label: s.label, accept: s.accept, form: 'array', persistAsEdge: false, numbered: s.numbered, max: s.max, caption: s.caption })),
    ...(sourceVideoSlot ? [{ key: sourceVideoSlot.metaKey, label: sourceVideoSlot.label, accept: 'video', form: 'single', persistAsEdge: false, numbered: false, max: 1 } as AssetSlot] : []),
  ]
  const assetValuesByKey: Record<string, string | string[]> = {}
  for (const s of imageUrlSlots) {
    const edgeSource = getEdgeSourceForSlot(s.group, edges, node.id)
    const nodeRef = edgeSource || getSlotNodeRef(meta, s.key)
    const thumbNode = nodeRef ? nodes.find((n) => n.id === nodeRef) : undefined
    assetValuesByKey[s.key] = (thumbNode ? resultPreviewUrl(thumbNode) : null) || getSlotThumbUrl(meta, s.key, nodes) || readMeta(meta, s.key) || ''
  }
  for (const s of arraySlots) assetValuesByKey[s.metaKey] = readArchetypeArray(meta, s.metaKey)
  if (sourceVideoSlot) assetValuesByKey[sourceVideoSlot.metaKey] = readMeta(meta, sourceVideoSlot.metaKey) || ''

  const handleAssetPick = (slot: AssetSlot, asset: AssetRef) => {
    if (slot.form === 'array') {
      const arr = arraySlots.find((a) => a.metaKey === slot.key)
      if (arr) handleArrayAdd(arr, asset.renderUrl)
      setOpenSlotKey('')
      return
    }
    if (slot.persistAsEdge) {
      const img = imageUrlSlots.find((i) => i.key === slot.key)
      if (!img) return
      if (asset.source === 'canvas' && asset.origin.source === 'canvas') handleSlotAssignment(img, asset.origin.nodeId)
      else setSingleFrameUrlMeta(img, asset.renderUrl)
      return
    }
    updateMeta({ [slot.key]: asset.renderUrl })
    setOpenSlotKey('')
  }
  const handleAssetUpload = async (slot: AssetSlot, file: File) => {
    if (slot.form === 'array') {
      const arr = arraySlots.find((a) => a.metaKey === slot.key)
      if (arr) await handleArrayUpload(arr, file)
      return
    }
    if (slot.persistAsEdge) {
      const img = imageUrlSlots.find((i) => i.key === slot.key)
      if (img) await handleSlotUpload(img, file)
      return
    }
    await handleSourceVideoUpload(slot.key, file)
  }
  // 同槽内拖拽重排:移动 referenceXxxUrls 数组项(单源 setArrayValue 写入);character{N} 编号由
  // projectPromptForSend 按新数组位置自动重算(单源,无需手动改 prompt/chip)。
  const handleReorder = (slot: AssetSlot, from: number, to: number) => {
    if (slot.form !== 'array') return
    const arr = readArchetypeArray(node.meta || {}, slot.key)
    if (from === to || from < 0 || to < 0 || from >= arr.length || to >= arr.length) return
    setArrayValue(slot.key, moveArrayItem(arr, from, to))
  }
  const handleBrowseAll = () => {
    setOpenSlotKey('')
    window.dispatchEvent(new CustomEvent('nomi-open-files-panel'))
  }
  const handleAssetRemove = (slot: AssetSlot, index: number) => {
    if (slot.form === 'array') { handleArrayRemove(slot.key, index); return }
    if (slot.persistAsEdge) {
      const img = imageUrlSlots.find((i) => i.key === slot.key)
      if (img) handleSlotAssignment(img, '')
      return
    }
    updateMeta({ [slot.key]: null })
  }

  // section="parameters"：底栏 = 模型芯片 + 该模型所有参数横排内联（实现见 InlineParameterBar）。
  if (section === 'parameters') {
    return (
      <InlineParameterBar
        modelOptions={modelOptions}
        modelCatalogStatus={modelCatalogStatus}
        renderedControls={renderedControls}
        selectedModelOption={selectedModelOption}
        archetype={archetype}
        meta={meta}
        onModelChange={handleModelChange}
        onCatalogControlChange={handleCatalogControlChange}
        onParameterControlChange={handleParameterControlChange}
      />
    )
  }

  // 模式分段切换要常驻（即便当前模式无参考槽，如纯文生）——有 modeBar / 数组槽 / 源视频槽都不空返回。
  if (section === 'references' && imageUrlSlots.length === 0 && arraySlots.length === 0 && !sourceVideoSlot && !showModeBar) return null

  // 走到这里只剩 section="references"（parameters/settings 已提前 return；旧的 all/model/controls 网格
  // 渲染随设置弹层落地而删除——参数现在进设置弹层，模型进底栏芯片，不再有这套裸值网格，Rule 1/12）。
  const rootClassName = cn('generation-canvas-v2-node__ref-section', 'flex flex-col gap-[5px]')

  return (
    <div className={rootClassName} aria-label="参考素材">
      {showReferences && showModeBar ? (
        <ModeBar choices={modeChoices} activeId={archMode?.id || ''} onSelect={handleModeSwitch} />
      ) : null}

      {showReferences && assetSlots.length > 0 ? (
        <AssetReference
          slots={assetSlots}
          valuesByKey={assetValuesByKey}
          projectId={getDesktopActiveProjectId() || null}
          openSlotKey={openSlotKey}
          uploadingSlotKey={uploadingSlotKey || uploadingArrayKey}
          onTogglePicker={(key) => setOpenSlotKey((prev) => (prev === key ? '' : key))}
          onPick={handleAssetPick}
          onUpload={(slot, file) => { void handleAssetUpload(slot, file) }}
          onRemove={handleAssetRemove}
          onInsertMention={onInsertMention}
          onReorder={handleReorder}
          onBrowseAll={handleBrowseAll}
        />
      ) : null}

      {showReferences && uploadError ? (
        <div className={cn('text-workbench-danger text-micro leading-[1.25]')} role="alert">{uploadError}</div>
      ) : null}
    </div>
  )
}
