/*
 * @Author: ZhaoYing 
 * @Date: 2026-05-07 18:37:31 
 * @Last Modified by: ZhaoYing
 * @Last Modified time: 2026-05-07 18:51:58
 */
import { type FC, useState, useEffect } from 'react'
import { useTranslation } from 'react-i18next'
import { Button, Flex, Form, Input, InputNumber, Select, App, Checkbox } from 'antd'
import { Node } from '@antv/x6'
import copy from 'copy-to-clipboard'
import clsx from 'clsx'

import { nodeRun } from '@/api/application'
import CodeBlock from '@/components/Markdown/CodeBlock'
import RbCard from '@/components/RbCard/Card'
import styles from '../Properties/properties.module.css'
import ContextList from './ContextList'
import FileVarInput from './FileVarInput'
import type { Suggestion } from '../Editor/plugin/AutocompletePlugin'
import Markdown from '@/components/Markdown'
import RbAlert from '@/components/RbAlert'

interface RunResult {
  status: 'completed' | 'failed' | 'running';
  node_id?: string;
  node_type?: string;
  inputs?: Record<string, any>;
  outputs?: any;
  token_usage?: {
    prompt_tokens: number;
    completion_tokens: number;
    total_tokens: number;
  };
  elapsed_time?: number;
  error?: string | null;
}

interface SingleNodeRunProps {
  open: boolean;
  onClose: () => void
  selectedNode: Node
  appId: string
  variableList: Suggestion[]
}

const SingleNodeRun: FC<SingleNodeRunProps> = ({ open, onClose, selectedNode, appId, variableList }) => {
  const { t } = useTranslation()
  const { message } = App.useApp()
  const [form] = Form.useForm()
  const [loading, setLoading] = useState(false)
  const [result, setResult] = useState<RunResult | null>(null)

  const [isAutoRun, setIsAutoRun] = useState(false)

  const nodeData = selectedNode?.getData() || {}
  const nodeName = nodeData.name || t(`workflow.${nodeData.type}`)

  const isLlm = nodeData.type === 'llm'
  const hasContext = isLlm && nodeData.config.context.defaultValue

  // Recursively collect all {{nodeId.var}} references from nodeData, excluding conv. vars
  const extractVarRefs = (val: any, refs = new Set<string>()): Set<string> => {
    if (typeof val === 'string') {
      for (const m of val.matchAll(/\{\{([^}]+)\}\}/g))
        if (!m[1].startsWith('conv.') && m[1] !== 'context') {
          refs.add(m[1])
        }
    } else if (Array.isArray(val)) {
      val.forEach(v => extractVarRefs(v, refs))
    } else if (val && typeof val === 'object') {
      Object.values(val).forEach(v => extractVarRefs(v, refs))
    }
    return refs
  }

  const varRefs = extractVarRefs(nodeData)
  const visionInputRef = isLlm ? nodeData.config.vision_input?.defaultValue?.match(/\{\{([^}]+)\}\}/)?.[1] : undefined
  const contextInputRef = isLlm ? nodeData.config.context?.defaultValue?.match(/\{\{([^}]+)\}\}/)?.[1] : undefined
  const inputVars = variableList.filter(v => varRefs.has(v.value) && v.value !== visionInputRef && v.value !== contextInputRef)


  const handleRun = () => {
    form.validateFields()
      .then((values) => {
        const { inputs = {} } = values
        console.log('values', values)
        const params: Record<string, any> = {};
        Object.keys(inputs).forEach(key => {
          const value = inputs[key]

          if (typeof value === 'object') {
            params[key] = value.map((file: any) => {
              if (file.url) {
                return file
              } else {
                return {
                  type: file.type,
                  transfer_method: 'local_file',
                  upload_file_id: file.response.data.file_id
                }
              }
            })
          } else {
            params[key] = value;
          }
        })
        setLoading(true)
        setResult({ status: 'running' })

        if (hasContext) {
          const contextValues: string[] = form.getFieldValue('context') || []
          if (contextValues.length > 0) {
            params['context'] = contextValues.map(item => { try { return JSON.parse(item) } catch { return item } })
          }
        }

        nodeRun(appId, nodeData.id, { inputs: params, stream: false })
          .then(res => {
            setResult(res as RunResult)
          })
          .catch(err => {
            setResult({ status: 'failed', error: err.message })
            setLoading(false)
          })
          .finally(() => setLoading(false))
      })
  }

  const handleCopy = (val: string) => {
    copy(val)
    message.success(t('common.copySuccess'))
  }

  const statusColor = result?.status === 'completed' ? '#369F21' : result?.status === 'failed' ? '#FF5D34' : '#5B6167'

  useEffect(() => {
    if (open) {
      if (nodeData?.type === 'iteration' || inputVars.length < 1 && !hasContext && !(isLlm && nodeData?.config?.vision?.defaultValue)) {
        setIsAutoRun(true)
      }
    }
  }, [open, inputVars, isLlm, hasContext, nodeData?.type, nodeData?.config?.vision?.defaultValue])

  useEffect(() => {
    if (isAutoRun) {
      handleRun()
    }
  }, [isAutoRun])

  if (!open) return null

  return (
    // 与 Properties 完全相同的定位容器
    <div className={clsx('rb:h-[calc(100vh-88px)] rb:w-90 rb:absolute rb:right-0 rb:top-0 rb:bottom-2.5 rb:z-1002', styles.properties)}>
      {/* mask：仅覆盖 header 以下的区域，header 保持透明露出节点名 */}
      <div
        className="rb:absolute rb:inset-x-0 rb:bottom-0 rb:top-0 rb:rounded-xl rb:bg-[rgba(0,0,0,0.3)] rb:z-1002"
      />

      {/* SingleNodeRun 卡片，z-index 高于 mask */}
      <div className="rb:absolute rb:inset-x-0 rb:top-25.5 rb:bottom-0 rb:z-1003">
        <RbCard
          title={`${t('workflow.testRun')} ${nodeName}`}
          extra={
            <div
              className="rb:size-4 rb:cursor-pointer rb:bg-cover rb:bg-[url('@/assets/images/close.svg')]"
              onClick={onClose}
            />
          }
          headerType="borderless"
          headerClassName="rb:font-[MiSans-Bold] rb:font-bold rb:min-h-[48px]!"
          className="rb:h-full! rb:hover:shadow-none!"
          bodyClassName="rb:overflow-y-auto! rb:h-[calc(100%-48px)]! rb:px-3! rb:pt-0! rb:pb-3!"
        >
          <Form form={form} layout="vertical" size="small" className="rb:mb-0!">
            <Flex vertical gap={12}>
              {/* Variables */}
              {nodeData?.type !== 'iteration' && inputVars.length > 0 && (
                <Flex vertical gap={8}>
                  <div className="rb:text-[12px] rb:font-medium rb:text-[#5B6167]">{t('workflow.variables')}</div>
                  {inputVars.map(v => (
                    <Form.Item
                      key={v.value}
                      name={['inputs', v.value.replace('{{', '').replace('}}', '')]}
                      label={v.dataType.includes('boolean')
                        ? null
                        : <Flex gap={4} align="center" className="rb:text-[12px]">
                          {v.nodeData?.icon && <div className={`rb:size-3.5 rb:bg-cover ${v.nodeData.icon}`} />}
                          <span className="rb:font-medium">{v.nodeData?.name}</span>
                          <span className="rb:text-[#5B6167]">/</span>
                          <span className="rb:text-[#1677ff]">{v.label}</span>
                        </Flex>
                      }
                      // rules={[{
                      //   required: ['knowledge-retrieval', 'loop'].includes(nodeData.type) && !v.dataType.includes('boolean'),
                      //   message: ['array[string]', 'array[number]'].includes(v.dataType) && Array.isArray(v.default) && v.default.length > 0 ? t('common.selectPlaceholder', { title: v.label }) : t('common.inputPlaceholder', { title: v.label })
                      // }]}
                      className="rb:mb-0!"
                    >
                      {['array[string]', 'array[number]'].includes(v.dataType) && Array.isArray(v.default) && v.default.length > 0
                      ? <Select
                        placeholder={t('common.pleaseSelect')}
                        options={v.default.map((item: string) => ({ label: item, value: item }))}
                      />
                      : v.dataType.includes('string') && nodeData.type === 'knowledge-retrieval'
                        ? <Input.TextArea
                          placeholder={t('common.pleaseEnter')}
                          size="small"
                        />
                      : v.dataType.includes('string')
                        ? <Input
                          placeholder={t('common.pleaseEnter')}
                          size="small"
                        />
                      : v.dataType.includes('number')
                        ? <InputNumber
                          size="small"
                          placeholder={t('common.pleaseEnter')}
                          className="rb:w-full!"
                          onChange={(value) => form.setFieldValue(['retry', 'retry_interval'], value)}
                        />
                      : v.dataType.includes('file')
                        ? <FileVarInput name={['inputs', v.value.replace('{{', '').replace('}}', '')]} dataType={v.dataType} form={form} />
                      : v.dataType.includes('boolean')
                        ? <Checkbox>
                          <Flex gap={4} align="center" className="rb:text-[12px]">
                          {v.nodeData?.icon && <div className={`rb:size-3.5 rb:bg-cover ${v.nodeData.icon}`} />}
                          <span className="rb:font-medium">{v.nodeData?.name}</span>
                          <span className="rb:text-[#5B6167]">/</span>
                          <span className="rb:text-[#1677ff]">{v.label}</span>
                        </Flex>
                        </Checkbox>
                        : null
                      }
                    </Form.Item>
                  ))}
                </Flex>
              )}
              {/* Context */}
              {hasContext && <ContextList />}

              {isLlm && nodeData?.config?.vision?.defaultValue && (() => {
                const ref = nodeData.config.vision_input?.defaultValue
                const visionVar = ref ? variableList.find(v => v.value === ref) : undefined
                const dataType = visionVar?.dataType ?? 'array[file]'

                // if (!visionVar) return null
                console.log('visionVar', ref)
                return (
                  <Form.Item
                    name={['inputs', ref.replace('{{', '').replace('}}', '')]}
                    label={t('workflow.config.llm.vision')}
                    className="rb:mb-0!"
                  >
                    <FileVarInput name={['inputs', ref.replace('{{', '').replace('}}', '')]} dataType={dataType} form={form} />
                  </Form.Item>
                )
              })()}

              {/* Run button */}
              {(!isAutoRun || result?.status) &&
                <Button type="primary" block onClick={handleRun} loading={!result?.status && loading} disabled={loading}>
                  {result?.status ? t('workflow.reStartRun') : t('workflow.startRun')}
                </Button>
              }

              {/* Status row */}
              {result && (
                <div className="rb:rounded-lg rb:border rb:border-[#E8E8E8] rb:p-3 rb:bg-[#F6FFF4]">
                  <Flex justify="space-between" align="start">
                    <Flex vertical align="start" gap={2}>
                      <span className="rb:text-[11px] rb:text-[#5B6167]">{t('workflow.status')}</span>
                      <span className="rb:font-medium rb:text-[13px]" style={{ color: statusColor }}>
                        {result.status?.toUpperCase()}
                      </span>
                    </Flex>
                    <Flex vertical align="start" gap={2}>
                      <span className="rb:text-[11px] rb:text-[#5B6167]">{t('workflow.elapsedTime')}</span>
                      {result.elapsed_time != null && <span className="rb:font-medium rb:text-[13px]">{result.elapsed_time?.toFixed(3)}ms</span>}
                    </Flex>
                    <Flex vertical gap={2} align="start">
                      <span className="rb:text-[11px] rb:text-[#5B6167]">{t('workflow.totalTokens')}</span>
                      {!loading && <span className="rb:font-medium rb:text-[13px]">{ result?.token_usage?.total_tokens || 0} Tokens</span>}
                    </Flex>
                  </Flex>
                </div>
              )}

              {/* Input / Output code blocks */}
              {result && (['inputs', 'process', 'outputs'] as const).map(key => {
                if (nodeData.type !== 'http-request' && key === 'process') return null
                const content = typeof result[key as keyof RunResult] === 'object' && result[key as keyof RunResult] ? JSON.stringify(result[key as keyof RunResult], null, 2) : result[key as keyof RunResult] ? result[key as keyof RunResult] : '{}'
                return (
                  <div key={key} className="rb:bg-[#EBEBEB] rb:rounded-lg">
                    <div className="rb:py-2 rb:px-3 rb:flex rb:justify-between rb:items-center rb:text-[12px]">
                      {t(`workflow.${key}_result`)}
                      <Button
                        className="rb:py-0! rb:px-1! rb:text-[12px]!"
                        size="small"
                        onClick={() => handleCopy(content)}
                      >{t('common.copy')}</Button>
                    </div>
                    <div className="rb:max-h-40 rb:overflow-auto">
                      <CodeBlock
                        size="small"
                        value={content}
                        needCopy={false}
                        showLineNumbers={true}
                        background="#EBEBEB"
                      />
                    </div>
                  </div>
                )
              })}

              {/* Error */}
              {result?.error && (
                <RbAlert color="orange" className="rb:pb-0!">
                  <Flex vertical className="rb:w-full!">
                    <Flex align="center" justify="space-between">
                      {t(`workflow.error`)}
                      <Button
                        className="rb:py-0! rb:px-1! rb:text-[12px]!"
                        size="small"
                        onClick={() => handleCopy(result?.error || '')}
                      >{t('common.copy')}</Button>
                    </Flex>
                    <Markdown className="rb:wrap-break-word!" content={result?.error || ''} />
                  </Flex>
                </RbAlert>
              )}
            </Flex>
          </Form>
        </RbCard>
      </div>
    </div>
  )
}

export default SingleNodeRun
