<script setup lang="ts">
import type { ModelAbilityItem } from '#/views/ai/model/modelUtils/model-ability';

import { computed, reactive, ref, watch } from 'vue';

import { EasyFlowFormModal } from '@easyflow/common-ui';
import { IconifyIcon } from '@easyflow/icons';

import {
  ElForm,
  ElFormItem,
  ElInput,
  ElMessage,
  ElOption,
  ElSelect,
} from 'element-plus';

import { api } from '#/api/request';
import { $t } from '#/locales';
import {
  getDefaultModelAbility,
  syncTagSelectedStatus as syncTagSelectedStatusUtil,
} from '#/views/ai/model/modelUtils/model-ability';
import {
  generateFeaturesFromModelAbility,
  resetModelAbility,
} from '#/views/ai/model/modelUtils/model-ability-utils';

type AgentHttpVersionPolicy = 'AUTO' | 'HTTP_1_1' | 'HTTP_2_PREFERRED';

interface ModelOptions {
  agentHttpVersionPolicy: AgentHttpVersionPolicy;
  [key: string]: unknown;
}

interface FormData {
  id?: string;
  modelType: string;
  title: string;
  modelName: string;
  groupName: string;
  providerId: string;
  invokeCode: string;
  publishEnabled: boolean;
  apiKey: string;
  endpoint: string;
  requestPath: string;
  supportThinking: boolean;
  supportTool: boolean;
  supportImage: boolean;
  supportAudio: boolean;
  supportFree: boolean;
  supportVideo: boolean;
  supportImageB64Only: boolean;
  supportToolMessage: boolean;
  options: ModelOptions;
}

const props = defineProps({
  providerId: {
    type: String,
    default: '',
  },
});

const emit = defineEmits(['reload']);
const selectedProviderId = ref<string>(props.providerId ?? '');

watch(
  () => props.providerId,
  (newVal) => {
    if (newVal) {
      selectedProviderId.value = newVal;
    }
  },
  { immediate: true },
);

const formDataRef = ref();
const isAdd = ref(true);
const dialogVisible = ref(false);
const btnLoading = ref(false);

const formData = reactive<FormData>({
  modelType: '',
  title: '',
  modelName: '',
  groupName: '',
  providerId: '',
  invokeCode: '',
  publishEnabled: false,
  apiKey: '',
  endpoint: '',
  requestPath: '',
  supportThinking: false,
  supportTool: false,
  supportImage: false,
  supportAudio: false,
  supportFree: false,
  supportVideo: false,
  supportImageB64Only: false,
  supportToolMessage: true,
  options: {
    agentHttpVersionPolicy: 'AUTO',
  },
});

const agentHttpVersionOptions = [
  { label: '自动', value: 'AUTO' },
  { label: 'HTTP/1.1 兼容', value: 'HTTP_1_1' },
  { label: 'HTTP/2 优先', value: 'HTTP_2_PREFERRED' },
] as const;

const normalizeAgentHttpVersionPolicy = (
  value?: unknown,
): AgentHttpVersionPolicy => {
  if (value === 'HTTP_1_1' || value === 'HTTP_2_PREFERRED') {
    return value;
  }
  return 'AUTO';
};

const normalizeModelOptions = (options?: unknown): ModelOptions => {
  const source =
    options && typeof options === 'object' && !Array.isArray(options)
      ? (options as Record<string, unknown>)
      : {};
  return {
    ...source,
    agentHttpVersionPolicy: normalizeAgentHttpVersionPolicy(
      source.agentHttpVersionPolicy,
    ),
  };
};

const modelAbility = ref<ModelAbilityItem[]>(getDefaultModelAbility());
const visibleModelAbility = computed(() =>
  modelAbility.value.filter(
    (item) => item.field !== 'supportImageB64Only' || formData.supportImage,
  ),
);
type SelectableModelType = '' | 'embeddingModel' | 'rerankModel';

const selectedModelType = ref<SelectableModelType>('');
const modelTypeAbilityOptions = [
  {
    label: $t('llmProvider.embeddingModel'),
    value: 'embeddingModel',
  },
  {
    label: $t('llmProvider.rerankModel'),
    value: 'rerankModel',
  },
] as const;
const hasSpecialModelType = computed(() => Boolean(selectedModelType.value));
const abilityIconMap: Record<string, string> = {
  embeddingModel: 'svg:knowledge',
  rerankModel: 'svg:data-center',
  thinking: 'svg:llm',
  tool: 'svg:wrench',
  video: 'mdi:video-outline',
  image: 'mdi:image-outline',
  audio: 'mdi:microphone-outline',
  imageB64: 'mdi:file-image-outline',
  toolMessage: 'mdi:hammer',
};

const syncTagSelectedStatus = () => {
  syncTagSelectedStatusUtil(modelAbility.value, formData);
};

const resetAbilitySelection = () => {
  resetModelAbility(modelAbility.value);
  syncTagSelectedStatus();
};

const handleTagClick = (item: ModelAbilityItem) => {
  if (hasSpecialModelType.value) {
    return;
  }
  item.selected = !item.selected;
  formData[item.field] = item.selected;
  if (item.field === 'supportImage' && !item.selected) {
    formData.supportImageB64Only = false;
    const base64Ability = modelAbility.value.find(
      (ability) => ability.field === 'supportImageB64Only',
    );
    if (base64Ability) base64Ability.selected = false;
  }
};

const handleModelTypeChipClick = (
  modelType: Exclude<SelectableModelType, ''>,
) => {
  const nextType = selectedModelType.value === modelType ? '' : modelType;
  selectedModelType.value = nextType;
  if (nextType) {
    resetAbilitySelection();
  }
};

const isAbilityChipDisabled = () => hasSpecialModelType.value;
const getAbilityIcon = (value: string) => abilityIconMap[value] || 'svg:llm';

const resolveModelType = (): FormData['modelType'] => {
  return selectedModelType.value || 'chatModel';
};

const normalizeSelectableModelType = (
  modelType?: string,
): SelectableModelType => {
  return modelType === 'embeddingModel' || modelType === 'rerankModel'
    ? modelType
    : '';
};

const resetFormData = () => {
  Object.assign(formData, {
    id: '',
    modelType: '',
    title: '',
    modelName: '',
    groupName: '',
    providerId: '',
    invokeCode: '',
    publishEnabled: false,
    apiKey: '',
    endpoint: '',
    requestPath: '',
    supportThinking: false,
    supportTool: false,
    supportAudio: false,
    supportVideo: false,
    supportImage: false,
    supportImageB64Only: false,
    supportFree: false,
    supportToolMessage: true,
    options: normalizeModelOptions(),
  });
};

defineExpose({
  openAddDialog(modelType?: string) {
    isAdd.value = true;
    formDataRef.value?.resetFields();
    resetFormData();
    selectedModelType.value = normalizeSelectableModelType(modelType);
    if (selectedModelType.value) {
      resetAbilitySelection();
    } else {
      syncTagSelectedStatus();
    }
    dialogVisible.value = true;
  },

  openEditDialog(item: any) {
    dialogVisible.value = true;
    isAdd.value = false;
    resetFormData();
    Object.assign(formData, {
      id: item.id,
      modelType: item.modelType || '',
      title: item.title || '',
      modelName: item.modelName || '',
      groupName: item.groupName || '',
      providerId: item.providerId || '',
      invokeCode: item.invokeCode || '',
      publishEnabled: item.publishEnabled || false,
      endpoint: item.endpoint || '',
      requestPath: item.requestPath || '',
      apiKey: item.apiKey || '',
      supportThinking: item.supportThinking || false,
      supportAudio: item.supportAudio || false,
      supportImage: item.supportImage || false,
      supportImageB64Only: item.supportImageB64Only || false,
      supportVideo: item.supportVideo || false,
      supportTool: item.supportTool || false,
      supportFree: item.supportFree || false,
      supportToolMessage:
        item.supportToolMessage === undefined ? true : item.supportToolMessage,
      options: normalizeModelOptions(item.options),
    });
    selectedModelType.value = normalizeSelectableModelType(item.modelType);
    if (selectedModelType.value) {
      resetAbilitySelection();
    } else {
      syncTagSelectedStatus();
    }
  },
});

const closeDialog = () => {
  dialogVisible.value = false;
};

const rules = {
  title: [{ required: true, message: $t('message.required'), trigger: 'blur' }],
  modelName: [
    { required: true, message: $t('message.required'), trigger: 'blur' },
  ],
  groupName: [
    { required: true, message: $t('message.required'), trigger: 'blur' },
  ],
};

const save = async () => {
  btnLoading.value = true;
  const modelType = resolveModelType();
  const features = generateFeaturesFromModelAbility(modelAbility.value);

  if (modelType !== 'chatModel') {
    for (const key of Object.keys(features) as Array<keyof typeof features>) {
      features[key] = false;
    }
  }

  try {
    await formDataRef.value.validate();
    const submitData = {
      ...formData,
      ...features,
      modelType,
      providerId: isAdd.value ? selectedProviderId.value : formData.providerId,
    };

    const url = isAdd.value ? '/api/v1/model/save' : '/api/v1/model/update';
    const res = await api.post(url, submitData);

    if (res.errorCode === 0) {
      ElMessage.success(res.message);
      emit('reload');
      closeDialog();
    } else {
      ElMessage.error(res.message || $t('ui.actionMessage.operationFailed'));
    }
  } catch (error) {
    if (!(error as any)?.fields) {
      ElMessage.error($t('ui.actionMessage.operationFailed'));
    }
  } finally {
    btnLoading.value = false;
  }
};
</script>

<template>
  <EasyFlowFormModal
    v-model:open="dialogVisible"
    :centered="true"
    :closable="!btnLoading"
    :title="isAdd ? '添加模型' : '编辑模型'"
    :before-close="closeDialog"
    width="640"
    :confirm-loading="btnLoading"
    :confirm-text="$t('button.save')"
    :submitting="btnLoading"
    @confirm="save"
  >
    <div class="model-modal">
      <ElForm
        ref="formDataRef"
        :model="formData"
        status-icon
        :rules="rules"
        label-position="top"
        class="model-modal__form"
      >
        <div class="model-modal__section">
          <ElFormItem prop="title" :label="$t('llm.title')">
            <ElInput
              v-model.trim="formData.title"
              placeholder="例如：生产主模型"
            />
          </ElFormItem>
          <ElFormItem prop="modelName" :label="$t('llm.llmModel')">
            <ElInput
              v-model.trim="formData.modelName"
              placeholder="例如：gpt-4.1 / glm-4.5 / qwen3:8b"
            />
          </ElFormItem>
          <ElFormItem prop="groupName" :label="$t('llm.groupName')">
            <ElInput
              v-model.trim="formData.groupName"
              placeholder="例如：默认组"
            />
          </ElFormItem>

          <ElFormItem
            :label="$t('llm.ability')"
            class="model-modal__ability-item"
          >
            <div class="model-modal__ability-panel">
              <div class="model-modal__ability-toolbar">
                <button
                  v-for="item in modelTypeAbilityOptions"
                  :key="item.value"
                  type="button"
                  class="model-modal__ability-chip"
                  :class="[
                    `is-tone-${item.value}`,
                    { 'is-active': selectedModelType === item.value },
                  ]"
                  @click="handleModelTypeChipClick(item.value)"
                >
                  <IconifyIcon
                    :icon="getAbilityIcon(item.value)"
                    class="model-modal__ability-icon"
                  />
                  {{ item.label }}
                </button>
                <span
                  class="model-modal__ability-separator"
                  aria-hidden="true"
                ></span>
                <button
                  v-for="item in visibleModelAbility"
                  :key="item.value"
                  type="button"
                  class="model-modal__ability-chip"
                  :class="{
                    'is-active': item.selected,
                    'is-disabled': isAbilityChipDisabled(),
                    [`is-tone-${item.value}`]: true,
                  }"
                  :disabled="isAbilityChipDisabled()"
                  @click="handleTagClick(item)"
                >
                  <IconifyIcon
                    :icon="getAbilityIcon(item.value)"
                    class="model-modal__ability-icon"
                  />
                  {{ item.label }}
                </button>
              </div>
            </div>
          </ElFormItem>

          <ElFormItem v-if="!hasSpecialModelType" label="Agent HTTP 传输">
            <ElSelect
              v-model="formData.options.agentHttpVersionPolicy"
              aria-label="Agent HTTP 传输"
            >
              <ElOption
                v-for="item in agentHttpVersionOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </ElSelect>
          </ElFormItem>
        </div>
      </ElForm>
    </div>
  </EasyFlowFormModal>
</template>

<style scoped>
.model-modal {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.model-modal__section {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 18px;
  background: hsl(var(--surface-panel) / 96%);
  border: 1px solid hsl(var(--divider-faint) / 42%);
  border-radius: 20px;
}

.model-modal__form {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

.model-modal__ability-item {
  margin-top: 4px;
}

.model-modal__ability-panel {
  padding: 2px;
  overflow: hidden;
  border-radius: 18px;
}

.model-modal__ability-toolbar {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  align-items: center;
}

.model-modal__ability-separator {
  align-self: stretch;
  width: 1px;
  min-height: 28px;
  background: hsl(var(--divider-faint) / 62%);
  border-radius: 999px;
}

.model-modal__ability-chip {
  display: inline-flex;
  gap: 8px;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding: 9px 18px;
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  color: hsl(var(--text-muted));
  cursor: pointer;
  background: hsl(var(--surface-contrast-soft) / 86%);
  border: 1px solid transparent;
  border-radius: 999px;
  transition:
    transform 0.2s ease,
    border-color 0.2s ease,
    color 0.2s ease,
    background 0.2s ease,
    box-shadow 0.2s ease;
}

.model-modal__ability-chip:hover:not(:disabled),
.model-modal__ability-chip:focus-visible:not(:disabled) {
  color: hsl(var(--text-strong));
  box-shadow: 0 10px 18px -14px hsl(var(--foreground) / 28%);
  transform: translateY(-1px);
}

.model-modal__ability-chip.is-active {
  color: hsl(var(--text-strong));
  background: hsl(var(--primary) / 10%);
  border-color: hsl(var(--primary) / 18%);
  box-shadow: inset 0 0 0 1px hsl(var(--primary) / 14%);
}

.model-modal__ability-chip.is-disabled {
  cursor: not-allowed;
  box-shadow: none;
  opacity: 0.56;
  transform: none;
}

.model-modal__ability-icon {
  font-size: 15px;
  opacity: 0.88;
}

.model-modal__ability-chip.is-active.is-tone-embeddingModel,
.model-modal__ability-chip.is-active.is-tone-thinking,
.model-modal__ability-chip.is-active.is-tone-toolMessage {
  color: hsl(var(--primary));
  background: hsl(var(--primary) / 10%);
  border-color: hsl(var(--primary) / 18%);
  box-shadow: inset 0 0 0 1px hsl(var(--primary) / 14%);
}

.model-modal__ability-chip.is-active.is-tone-rerankModel,
.model-modal__ability-chip.is-active.is-tone-tool {
  color: hsl(var(--warning));
  background: hsl(var(--warning) / 12%);
  border-color: hsl(var(--warning) / 18%);
  box-shadow: inset 0 0 0 1px hsl(var(--warning) / 14%);
}

.model-modal__ability-chip.is-active.is-tone-image,
.model-modal__ability-chip.is-active.is-tone-imageB64 {
  color: hsl(var(--success));
  background: hsl(var(--success) / 12%);
  border-color: hsl(var(--success) / 18%);
  box-shadow: inset 0 0 0 1px hsl(var(--success) / 14%);
}

.model-modal__ability-chip.is-active.is-tone-audio,
.model-modal__ability-chip.is-active.is-tone-video,
.model-modal__ability-chip.is-active.is-tone-free {
  color: hsl(var(--danger));
  background: hsl(var(--danger) / 10%);
  border-color: hsl(var(--danger) / 16%);
  box-shadow: inset 0 0 0 1px hsl(var(--danger) / 12%);
}

@media (max-width: 640px) {
  .model-modal__ability-separator {
    display: none;
  }
}
</style>
