export type CapabilityEditorState = {
  types: string[];
  flags: Record<CapabilityFlagKey, boolean>;
  limits: Record<CapabilityLimitKey, string>;
  options: Record<CapabilityOptionKey, string>;
  typeConfigs: Record<string, Record<string, unknown>>;
  extra: Record<string, unknown>;
};

export type CapabilityFlagKey =
  | 'stream'
  | 'vision'
  | 'reasoning'
  | 'multimodal'
  | 'supportTool'
  | 'supportThinking'
  | 'supportThinkingModeSwitch'
  | 'supportStructuredOutput'
  | 'supportWebSearch'
  | 'supportQualityControl'
  | 'inputMultipleImages'
  | 'outputMultipleImages'
  | 'supportBase64Input'
  | 'supportUrlInput'
  | 'outputAudio';

export type CapabilityLimitKey =
  | 'maxContextTokens'
  | 'maxInputTokens'
  | 'maxOutputTokens'
  | 'maxThinkingTokens'
  | 'inputMaxImagesCount'
  | 'outputMaxImagesCount'
  | 'outputMaxSize'
  | 'durationMin'
  | 'durationMax';

export type CapabilityOptionKey = 'outputResolutions' | 'aspectRatios' | 'thinkingEfforts' | 'embeddingDimensions';

export type CapabilityConfigArea = 'text' | 'embedding' | 'image' | 'video' | 'audio' | 'digitalHuman' | 'model3d' | 'none';

export type PlatformModelTypeDefinition = {
  key: string;
  label: string;
  group: string;
  area: CapabilityConfigArea;
};

export const platformModelTypeDefinitions: PlatformModelTypeDefinition[] = [
  { key: 'text_generate', label: '文本生成', group: '文本', area: 'text' },
  { key: 'text_embedding', label: '文本向量', group: '文本', area: 'embedding' },
  { key: 'text_rerank', label: '重排序', group: '文本', area: 'text' },
  { key: 'image_generate', label: '图像生成', group: '图像', area: 'image' },
  { key: 'image_edit', label: '图像编辑', group: '图像', area: 'image' },
  { key: 'image_analysis', label: '图像理解', group: '理解', area: 'text' },
  { key: 'video_generate', label: '视频生成', group: '视频', area: 'video' },
  { key: 'image_to_video', label: '图生视频', group: '视频', area: 'video' },
  { key: 'video_edit', label: '视频编辑', group: '视频', area: 'video' },
  { key: 'omni_video', label: '多模态视频', group: '视频', area: 'video' },
  { key: 'video_understanding', label: '视频理解', group: '理解', area: 'text' },
  { key: 'audio_understanding', label: '音频理解', group: '理解', area: 'text' },
  { key: 'omni', label: '全模态理解', group: '理解', area: 'text' },
  { key: 'tools_call', label: '工具调用', group: '文本', area: 'text' },
  { key: 'audio_generate', label: '音频生成', group: '音频', area: 'audio' },
  { key: 'text_to_speech', label: '语音合成', group: '音频', area: 'audio' },
  { key: 'speech_to_text', label: '语音转写', group: '音频', area: 'audio' },
  { key: 'digital_human_generate', label: '数字人生成', group: '数字人', area: 'digitalHuman' },
  { key: 'text_to_model', label: '文生 3D', group: '3D', area: 'model3d' },
  { key: 'image_to_model', label: '图生 3D', group: '3D', area: 'model3d' },
  { key: 'multiview_to_model', label: '多视角 3D', group: '3D', area: 'model3d' },
  { key: 'mesh_edit', label: '3D 模型编辑', group: '3D', area: 'model3d' },
];

const modelTypeDefinitionMap = new Map(platformModelTypeDefinitions.map((item) => [item.key, item]));

const flagKeys: CapabilityFlagKey[] = [
  'stream',
  'vision',
  'reasoning',
  'multimodal',
  'supportTool',
  'supportThinking',
  'supportThinkingModeSwitch',
  'supportStructuredOutput',
  'supportWebSearch',
  'supportQualityControl',
  'inputMultipleImages',
  'outputMultipleImages',
  'supportBase64Input',
  'supportUrlInput',
  'outputAudio',
];

const limitKeys: CapabilityLimitKey[] = [
  'maxContextTokens',
  'maxInputTokens',
  'maxOutputTokens',
  'maxThinkingTokens',
  'inputMaxImagesCount',
  'outputMaxImagesCount',
  'outputMaxSize',
  'durationMin',
  'durationMax',
];

const optionKeys: CapabilityOptionKey[] = ['outputResolutions', 'aspectRatios', 'thinkingEfforts', 'embeddingDimensions'];
const managedRootKeys = new Set<string>([
  'originalTypes',
  'stream',
  'vision',
  'reasoning',
  'multimodal',
  'supportTool',
  'supportThinking',
  'supportThinkingModeSwitch',
  'supportStructuredOutput',
  'supportWebSearch',
  'supportBase64Input',
  'supportUrlInput',
  'supportQualityControl',
  'maxContextTokens',
  'maxInputTokens',
  'maxOutputTokens',
  'maxThinkingTokens',
  'thinkingEffortLevels',
]);

const managedNestedKeys = new Set<string>([
	'supportedApiProtocols',
  'supportTool',
  'supportThinking',
  'supportThinkingModeSwitch',
  'supportStructuredOutput',
  'supportWebSearch',
  'thinkingEffortLevels',
  'max_context_tokens',
  'max_input_tokens',
  'max_output_tokens',
  'max_thinking_tokens',
  'dimensions',
  'support_base64_input',
  'support_url_input',
  'support_quality_control',
  'input_multiple_images',
  'input_max_images_count',
  'output_multiple_images',
  'output_max_images_count',
  'output_max_size',
  'output_resolutions',
  'aspect_ratio_allowed',
  'aspect_ratio_range',
  'input_aspect_ratio_range',
  'duration_range',
  'output_audio',
]);

export function createCapabilityEditorState(modelType = 'text_generate'): CapabilityEditorState {
  return {
    types: [modelType].filter(Boolean),
    flags: Object.fromEntries(flagKeys.map((key) => [key, false])) as Record<CapabilityFlagKey, boolean>,
    limits: Object.fromEntries(limitKeys.map((key) => [key, ''])) as Record<CapabilityLimitKey, string>,
    options: Object.fromEntries(optionKeys.map((key) => [key, ''])) as Record<CapabilityOptionKey, string>,
    typeConfigs: modelType ? { [modelType]: defaultCapabilityConfig(modelType) } : {},
    extra: {},
  };
}

export function modelTypeLabel(type: string) {
  return modelTypeDefinitionMap.get(type)?.label ?? type;
}

export function modelTypeGroup(type: string) {
  return modelTypeDefinitionMap.get(type)?.group ?? '其他';
}

export function capabilityAreas(types: string[]) {
  const areas = new Set(types.map((type) => modelTypeDefinitionMap.get(type)?.area ?? inferArea(type)));
  return {
    hasText: areas.has('text'),
    hasEmbedding: areas.has('embedding'),
    hasImage: areas.has('image'),
    hasVideo: areas.has('video'),
    hasAudio: areas.has('audio'),
    hasDigitalHuman: areas.has('digitalHuman'),
    hasModel3d: areas.has('model3d'),
  };
}

export function capabilitiesToForm(value?: Record<string, unknown>, modelType = 'text_generate'): CapabilityEditorState {
  const state = createCapabilityEditorState(modelType);
  const source = isRecord(value) ? value : {};
  const sourceTypes = stringArray(source.originalTypes);
  const nestedTypes = Object.entries(source)
    .filter(([key, item]) => key !== 'originalTypes' && isRecord(item) && looksLikeCapabilityType(key))
    .map(([key]) => key);
  state.types = uniqueStrings([...sourceTypes, ...nestedTypes, modelType]);
  state.typeConfigs = Object.fromEntries(state.types.map((type) => [type, readCapabilityConfig(source, type, modelType)]));
  state.extra = Object.fromEntries(
    Object.entries(source).filter(([key]) => !managedRootKeys.has(key) && !state.types.includes(key)),
  );

  state.flags.stream = boolFrom(source.stream);
  state.flags.vision = boolFrom(source.vision);
  state.flags.reasoning = boolFrom(source.reasoning);
  state.flags.multimodal = boolFrom(source.multimodal);
  state.flags.supportTool = boolFrom(source.supportTool ?? nestedValue(source, 'supportTool'));
  state.flags.supportThinking = boolFrom(source.supportThinking ?? nestedValue(source, 'supportThinking'));
  state.flags.supportThinkingModeSwitch = boolFrom(source.supportThinkingModeSwitch ?? nestedValue(source, 'supportThinkingModeSwitch'));
  state.flags.supportStructuredOutput = boolFrom(source.supportStructuredOutput ?? nestedValue(source, 'supportStructuredOutput'));
  state.flags.supportWebSearch = boolFrom(source.supportWebSearch ?? nestedValue(source, 'supportWebSearch'));
  state.flags.supportBase64Input = boolFrom(source.supportBase64Input ?? nestedValue(source, 'support_base64_input'));
  state.flags.supportUrlInput = boolFrom(source.supportUrlInput ?? nestedValue(source, 'support_url_input'));
  state.flags.supportQualityControl = boolFrom(source.supportQualityControl ?? nestedValue(source, 'support_quality_control'));
  state.flags.inputMultipleImages = nestedBool(source, 'input_multiple_images');
  state.flags.outputMultipleImages = nestedBool(source, 'output_multiple_images');
  state.flags.outputAudio = nestedBool(source, 'output_audio');

  state.limits.maxContextTokens = stringValue(source.maxContextTokens ?? source.max_context_tokens ?? nestedValue(source, 'max_context_tokens'));
  state.limits.maxInputTokens = stringValue(source.maxInputTokens ?? source.max_input_tokens ?? nestedValue(source, 'max_input_tokens'));
  state.limits.maxOutputTokens = stringValue(source.maxOutputTokens ?? source.max_output_tokens ?? nestedValue(source, 'max_output_tokens'));
  state.limits.maxThinkingTokens = stringValue(source.maxThinkingTokens ?? source.max_thinking_tokens ?? nestedValue(source, 'max_thinking_tokens'));
  state.limits.inputMaxImagesCount = stringValue(nestedValue(source, 'input_max_images_count'));
  state.limits.outputMaxImagesCount = stringValue(nestedValue(source, 'output_max_images_count'));
  state.limits.outputMaxSize = stringValue(nestedValue(source, 'output_max_size'));

  const durationRange = nestedValue(source, 'duration_range');
  if (Array.isArray(durationRange)) {
    state.limits.durationMin = stringValue(durationRange[0]);
    state.limits.durationMax = stringValue(durationRange[1]);
  }
  state.options.outputResolutions = stringArray(nestedValue(source, 'output_resolutions')).join(', ');
  state.options.aspectRatios = stringArray(nestedValue(source, 'aspect_ratio_allowed')).join(', ');
  state.options.thinkingEfforts = stringArray(source.thinkingEffortLevels ?? nestedValue(source, 'thinkingEffortLevels')).join(', ');
  state.options.embeddingDimensions = stringArray(nestedValue(source, 'dimensions')).join(', ');
  return state;
}

export function syncPrimaryCapability(state: CapabilityEditorState, modelType: string): CapabilityEditorState {
  if (!modelType || state.types.includes(modelType)) return state;
  return addCapabilityType(state, modelType);
}

export function addCapabilityType(state: CapabilityEditorState, type: string): CapabilityEditorState {
  if (!type) return state;
  return {
    ...state,
    types: uniqueStrings([...state.types, type]),
    typeConfigs: {
      ...state.typeConfigs,
      [type]: state.typeConfigs[type] ?? defaultCapabilityConfig(type),
    },
  };
}

export function removeCapabilityType(state: CapabilityEditorState, type: string): CapabilityEditorState {
  const nextTypes = state.types.filter((item) => item !== type);
  if (!nextTypes.length) return state;
  const nextConfigs = { ...state.typeConfigs };
  delete nextConfigs[type];
  return { ...state, types: nextTypes, typeConfigs: nextConfigs };
}

export function updateCapabilityConfig(state: CapabilityEditorState, type: string, config: Record<string, unknown>): CapabilityEditorState {
  return { ...state, typeConfigs: { ...state.typeConfigs, [type]: config } };
}

export function capabilitiesFromForm(state: CapabilityEditorState): Record<string, unknown> {
  const output: Record<string, unknown> = { ...state.extra, originalTypes: uniqueStrings(state.types) };
  state.types.forEach((type) => {
    const config = normalizeCapabilityConfig(type, state.typeConfigs[type] ?? defaultCapabilityConfig(type));
    if (Object.keys(config).length > 0) output[type] = config;
  });
  return output;
}

export function defaultCapabilityConfig(type: string): Record<string, unknown> {
  if (isTextLike(type)) {
    return {
	  ...(type === 'text_generate' ? { supportedApiProtocols: ['openai_chat_completions'] } : {}),
      supportTool: type === 'tools_call',
      supportThinking: false,
      supportThinkingModeSwitch: false,
      supportStructuredOutput: false,
      supportWebSearch: false,
    };
  }
  if (type === 'text_embedding') return { dimensions: [] };
  if (type === 'image_generate') {
    return { output_resolutions: ['1K'], output_multiple_images: false, support_quality_control: false };
  }
  if (type === 'image_edit') {
    return { input_multiple_images: false, output_resolutions: ['1K'], output_multiple_images: false, support_quality_control: false };
  }
  if (type === 'video_generate') {
    return { output_resolutions: ['720p'], duration_range: [5, 10], output_audio: false };
  }
  if (type === 'image_to_video') {
    return {
      output_resolutions: ['720p'],
      input_first_frame: true,
      input_first_last_frame: false,
      input_last_frame: false,
      input_reference_generate_single: false,
      input_reference_generate_multiple: false,
      support_video_effect_template: false,
      output_audio: false,
      aspect_ratio_allowed: [],
    };
  }
  if (type === 'video_edit') {
    return { input_video_required: true, output_resolutions: ['720p'], duration_range: [5, 10], output_audio: false };
  }
  if (type === 'omni_video') {
    return {
      supported_modes: ['text_to_video'],
      output_resolutions: ['720p'],
      aspect_ratio_allowed: ['16:9'],
      duration_options: [5],
      output_audio: false,
      max_videos: 0,
      max_images: 0,
      max_elements: 0,
    };
  }
  if (type === 'text_to_model' || type === 'image_to_model' || type === 'multiview_to_model') {
    return { support_texture: false, support_part_generation: false };
  }
  return {};
}

function cleanNestedConfig(value: Record<string, unknown>) {
  return Object.fromEntries(Object.entries(value).filter(([key, item]) => !managedNestedKeys.has(key) || shouldKeepNestedValue(key, item)));
}

function readCapabilityConfig(source: Record<string, unknown>, type: string, modelType: string) {
  const nested = isRecord(source[type]) ? source[type] : {};
  const root = type === modelType ? rootCompatibilityConfig(source) : {};
  const hasSourceConfig = Object.keys(root).length > 0 || Object.keys(nested).length > 0;
  const base = hasSourceConfig ? {} : defaultCapabilityConfig(type);
  return normalizeCapabilityConfig(type, { ...base, ...root, ...nested });
}

function rootCompatibilityConfig(source: Record<string, unknown>) {
  const config: Record<string, unknown> = {};
  [
    'stream',
    'vision',
    'reasoning',
    'multimodal',
    'supportTool',
    'supportThinking',
    'supportThinkingModeSwitch',
    'supportStructuredOutput',
    'supportWebSearch',
    'supportBase64Input',
    'supportUrlInput',
    'supportQualityControl',
    'maxContextTokens',
    'maxInputTokens',
    'maxOutputTokens',
    'maxThinkingTokens',
    'thinkingEffortLevels',
  ].forEach((key) => {
    if (key in source) config[toCapabilityKey(key)] = source[key];
  });
  return config;
}

function toCapabilityKey(key: string) {
  const map: Record<string, string> = {
    supportBase64Input: 'support_base64_input',
    supportUrlInput: 'support_url_input',
    supportQualityControl: 'support_quality_control',
    maxContextTokens: 'max_context_tokens',
    maxInputTokens: 'max_input_tokens',
    maxOutputTokens: 'max_output_tokens',
    maxThinkingTokens: 'max_thinking_tokens',
  };
  return map[key] ?? key;
}

function normalizeCapabilityConfig(type: string, config: Record<string, unknown>) {
  return Object.fromEntries(Object.entries(config).filter(([key, value]) => shouldKeepConfigValue(type, key, value)));
}

function shouldKeepConfigValue(type: string, key: string, value: unknown) {
  if (value === undefined || value === null || value === '') return false;
  if (Array.isArray(value)) return value.length > 0 || defaultArrayKeys(type).has(key);
  if (isRecord(value)) return Object.keys(value).length > 0;
  return true;
}

function defaultArrayKeys(type: string) {
  if (type === 'text_embedding') return new Set(['dimensions']);
  if (type === 'image_to_video' || type === 'omni_video') return new Set(['aspect_ratio_allowed']);
  return new Set<string>();
}

function shouldKeepNestedValue(key: string, value: unknown) {
  if (key === 'output_resolutions' || key === 'aspect_ratio_allowed' || key === 'aspect_ratio_range' || key === 'input_aspect_ratio_range' || key === 'duration_range') {
    return !Array.isArray(value);
  }
  if (key === 'thinkingEffortLevels' || key === 'dimensions' || key === 'supportedApiProtocols') {
    return !Array.isArray(value);
  }
  if (key.endsWith('_count') || key === 'output_max_size' || key.endsWith('_tokens')) {
    return typeof value !== 'number' && typeof value !== 'string';
  }
  return typeof value !== 'boolean' && value !== 'true' && value !== 'false';
}

function nestedValue(source: Record<string, unknown>, key: string) {
  for (const type of stringArray(source.originalTypes)) {
    const config = isRecord(source[type]) ? source[type] : undefined;
    if (config && key in config) return config[key];
  }
  for (const value of Object.values(source)) {
    if (isRecord(value) && key in value) return value[key];
  }
  return undefined;
}

function nestedBool(source: Record<string, unknown>, key: string) {
  return boolFrom(nestedValue(source, key));
}

function setIfTruthy(target: Record<string, unknown>, key: string, value: boolean) {
  if (value) target[key] = true;
}

function setBoolean(target: Record<string, unknown>, key: string, value: boolean) {
  target[key] = value;
}

function setNumberIfPresent(target: Record<string, unknown>, key: string, value: string) {
  const parsed = Number(value);
  if (value.trim() && Number.isFinite(parsed)) target[key] = parsed;
}

function setArrayIfPresent(target: Record<string, unknown>, key: string, value: string) {
  const items = value.split(',').map((item) => item.trim()).filter(Boolean);
  if (items.length) target[key] = items;
}

function setNumberArrayIfPresent(target: Record<string, unknown>, key: string, value: string) {
  const items = value.split(',').map((item) => Number(item.trim())).filter(Number.isFinite);
  if (items.length) target[key] = items;
}

function durationRange(min: string, max: string) {
  const parsedMin = Number(min);
  const parsedMax = Number(max);
  if (!min.trim() && !max.trim()) return undefined;
  return [Number.isFinite(parsedMin) ? parsedMin : 0, Number.isFinite(parsedMax) ? parsedMax : parsedMin || 0];
}

function stringValue(value: unknown) {
  return value === undefined || value === null ? '' : String(value);
}

function stringArray(value: unknown) {
  if (!Array.isArray(value)) return [];
  return value.map(String).filter(Boolean);
}

function uniqueStrings(values: string[]) {
  return Array.from(new Set(values.map((item) => item.trim()).filter(Boolean)));
}

function boolFrom(value: unknown) {
  return value === true || value === 'true';
}

function looksLikeCapabilityType(key: string) {
  return modelTypeDefinitionMap.has(key) || key.includes('_') || ['chat', 'image', 'video', 'audio', 'embedding', 'rerank', 'music', 'digital_human', 'model_3d'].includes(key);
}

function inferArea(type: string): CapabilityConfigArea {
  if (isTextLike(type)) return 'text';
  if (type === 'text_embedding') return 'embedding';
  if (isImageLike(type)) return 'image';
  if (isVideoLike(type)) return 'video';
  if (type.includes('audio') || type.includes('speech')) return 'audio';
  if (type.includes('digital_human')) return 'digitalHuman';
  if (type.includes('model') || type.includes('mesh')) return 'model3d';
  return 'none';
}

function isTextLike(type: string) {
  return ['text_generate', 'text_rerank', 'image_analysis', 'video_understanding', 'audio_understanding', 'omni', 'tools_call'].includes(type);
}

function isImageLike(type: string) {
  return type.includes('image') || type === 'image';
}

function isVideoLike(type: string) {
  return type.includes('video') || type === 'video';
}

function isVisualMedia(type: string) {
  return isImageLike(type) || isVideoLike(type);
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && !Array.isArray(value) && typeof value === 'object';
}
