// ============================================================
// useModelUI — param_schema → UIFieldConfig[] 转换 Hook
//
// 职责：
//   - 遍历模型的 param_schema，解析 spec/ui 字段
//   - 查 WIDGET_MAP 获取对应的 React 组件
//   - 解析 constraints_config.requires 计算字段间依赖
//   - 组装出可直接渲染的 UIFieldConfig 数组
//
// ModelInputForm 只需遍历 fields 渲染 Form.Item 即可，
// 不需要关心 param_schema 的解析逻辑。
// ============================================================

import { useMemo } from 'react';
import type { ComponentType } from 'react';

import type { ModelsListResponseBody } from '@/modules/home/controllers/model-api';
import { WIDGET_MAP, FALLBACK_WIDGET } from '../views/left/controls';
import type { WidgetProps } from '../views/left/controls';

// ---------- 输出类型 ----------

/**
 * 单个字段的 UI 配置。
 *
 * ModelInputForm 遍历此数组，每一项渲染一个 Form.Item。
 */
export interface UIFieldConfig {
   /** 渲染顺序（param_schema 中的索引，保证稳定） */
   index: number;
   /** 表单字段名（map_to），对应 Form.Item name */
   name: string;
   /** API 声明的 widget 名称（如 "seedance2Content"），供提交时做特殊序列化 */
   widgetName: string;
   /** 要渲染的 React 组件 */
   component: ComponentType<WidgetProps>;
   /** 显示标签（ui.label → Form.Item label） */
   label: string;
   /** 是否必填（spec.required → Form.Item rules） */
   must: boolean;
   /** 默认值（spec.default → Form initialValues） */
   defaultValue?: unknown;
   /** 提示文本（ui.tips） */
   tips?: string;
   /** Form.Item 的 valuePropName（checkbox 用 'checked'，其他用默认 'value'） */
   valuePropName?: string;
   /** 是否为文件上传类控件（需要 getValueFromEvent 提取 fileList） */
   isFileWidget: boolean;
   /**
    * 条件依赖：当这些字段的值为空时，本字段应被禁用。
    *
    * 来源于 constraints_config.requires 的逆向推导：
    *   { if: "sref", then: ["sw", "sv"] }
    *   → sw.dependsOn = ["sref"], sv.dependsOn = ["sref"]
    */
   dependsOn?: string[];
   /** 传给 widget 组件的配置（placeholder/options/min/max 等） */
   widgetConfig: Record<string, unknown>;
}

// ---------- 文件类型 widget 集合 ----------

const FILE_WIDGETS = new Set(['imageInput', 'imageList', 'audioList', 'videoInput']);

// ---------- 约束解析 ----------

interface ConstraintRule {
   if: string;
   then: string[];
}

/**
 * 从 constraints_config.requires 构建反向依赖表。
 *
 * 输入：
 *   [{ if: "sref", then: ["sw", "sv"] }]
 *
 * 输出：
 *   { sw: ["sref"], sv: ["sref"] }
 *
 * 含义：sw 和 sv 依赖 sref，sref 为空时它们应被禁用。
 */
function buildDependencyMap(
   constraintsConfig: Record<string, unknown>,
): Map<string, string[]> {
   const depMap = new Map<string, string[]>();
   const requires = constraintsConfig.requires as ConstraintRule[] | undefined;

   if (!requires || !Array.isArray(requires)) return depMap;

   for (const rule of requires) {
      if (!rule.if || !rule.then || !Array.isArray(rule.then)) continue;
      for (const target of rule.then) {
         const existing = depMap.get(target) || [];
         existing.push(rule.if);
         depMap.set(target, existing);
      }
   }

   return depMap;
}

// ---------- Hook ----------

/**
 * 根据模型的 param_schema 和 constraints_config 构建 UI 字段配置数组。
 *
 * @param paramSchema — 来自 selectedModel.param_schema
 * @param constraintsConfig — 来自 selectedModel.constraints_config
 * @returns UIFieldConfig[] — 渲染就绪的字段配置列表
 */
export function useModelUI(
   paramSchema: ModelsListResponseBody['param_schema'],
   constraintsConfig: Record<string, unknown> = {},
): UIFieldConfig[] {
   return useMemo(() => {
      if (!paramSchema || paramSchema.length === 0) return [];

      const depMap = buildDependencyMap(constraintsConfig);

      return paramSchema.map((item, index) => {
         const spec = (item.spec || {}) as Record<string, unknown>;
         const ui = (item.ui || {}) as Record<string, unknown>;
         const widget = (ui.widget as string) || 'lineEdit';

         const component = WIDGET_MAP[widget] || FALLBACK_WIDGET;

         const widgetConfig: Record<string, unknown> = {
            placeholder: ui.placeholder || `请输入${ui.label || item.map_to}`,
            required: spec.required,
            enumValues: spec.enum,
            minValue: spec.min_value,
            maxValue: spec.max_value,
            step: spec.single_step,
            minLength: spec.min_length,
            maxLength: spec.max_length,
            // imageInput / imageList / audioList
            fileFilter: spec.file_filter,
            maxFileSizeMb: spec.max_file_size_mb,
            minFiles: spec.min_files,
            maxFiles: spec.max_files,
            enableSwitch: ui.enable_switch,
            // seedance2Content：spec 中的媒体文件数量 + 上传供应商
            imageFiles: (spec.image_files as number) ?? 0,
            videoFiles: (spec.video_files as number) ?? 0,
            audioFiles: (spec.audio_files as number) ?? 0,
            uploadProvider: spec.upload_provider as string | undefined,
         };

         const isFile = FILE_WIDGETS.has(widget);
         const isCheckbox = widget === 'checkbox';
         const name = item.map_to;
         const dependsOn = depMap.get(name);

         return {
            index,
            name,
            widgetName: widget,
            component,
            label: (ui.label as string) || name,
            must: (spec.required as boolean) || false,
            defaultValue: spec.default,
            tips: (ui.tips as string) || undefined,
            valuePropName: isCheckbox ? 'checked' : undefined,
            isFileWidget: isFile,
            dependsOn: dependsOn && dependsOn.length > 0 ? dependsOn : undefined,
            widgetConfig,
         };
      });
   }, [paramSchema, constraintsConfig]);
}

/** 判断 widget 类型是否为文件上传类控件 */
export function isFileWidget(widgetName: string): boolean {
   return FILE_WIDGETS.has(widgetName);
}

// ---------- 值判空工具 ----------

/**
 * 判断表单值是否为"空"（用于依赖禁用判断）。
 *
 * 注意：空数组 [] 不视为空。
 * 对于 enable_switch 控件：undefined = 开关关闭, [] = 开关开启但未选文件。
 * 配合 constraints_config.requires：开关开启即解锁依赖字段，无需等待文件上传。
 */
export function isValueEmpty(value: unknown): boolean {
   if (value === undefined || value === null) return true;
   if (typeof value === 'string' && value.trim() === '') return true;
   if (typeof value === 'boolean' && !value) return true;
   return false;
}
