<!-- 模型配置对话框，负责维护创作模型参数与各业务提示词预设。 -->
<script setup lang="ts">
import { computed, reactive, ref, watch } from "vue";
import { Delete, Edit, Plus, Setting } from "@element-plus/icons-vue";
import type { CreativeOptions, ModelConfig, PromptPreset } from "@/api/creative";
import CreativeSelect, { type CreativeSelectOption } from "@/components/creative/CreativeSelect.vue";
import { modelPriceLabel } from "@/utils/modelPricing";

/** 模型配置支持的业务用途。 */
type Purpose = "prompt_reverse" | "image_generation" | "video_generation";
/** 单个模型可保存的生成参数。 */
type Settings = { aspect_ratio?: string; resolution?: string; prompt?: string };
/** 单个用途提交给父组件的模型配置。 */
type Value = { model_id: string; settings: Settings };

const props = withDefaults(
  defineProps<{
    modelValue: boolean;
    options: CreativeOptions;
    configs: ModelConfig[];
    saving?: boolean;
    prompts?: PromptPreset[];
    promptSelections?: Record<string, string>;
    projectType?: "video_redraw" | "premium_drama" | "script_analysis";
    analysisOnly?: boolean;
  }>(),
  { saving: false, prompts: () => [], promptSelections: () => ({}), projectType: "video_redraw" },
);
const emit = defineEmits<{
  "update:modelValue": [value: boolean];
  save: [payload: Record<Purpose, Value>];
  "prompt-select": [payload: { purpose: Purpose; type: string; promptId: string; content: string }];
  "prompt-create": [payload: { name: string; type: string; content: string }];
  "prompt-update": [payload: { id: string; name: string; type: string; content: string }];
  "prompt-delete": [prompt: PromptPreset];
}>();

const fields = computed(() => {
  const items = [
    {
      purpose: "prompt_reverse" as Purpose,
      label: props.projectType === "video_redraw" ? "反推模型" : "文本模型",
      type: "text",
    },
  ];
  if (!props.analysisOnly)
    items.push(
      { purpose: "image_generation" as Purpose, label: "图片模型", type: "image" },
      { purpose: "video_generation" as Purpose, label: "视频模型", type: "video" },
    );
  return items;
});
const form = reactive<Record<Purpose, Value>>({
  prompt_reverse: { model_id: "", settings: { prompt: "" } },
  image_generation: { model_id: "", settings: { aspect_ratio: "16:9", resolution: "1k", prompt: "" } },
  video_generation: { model_id: "", settings: { aspect_ratio: "16:9", resolution: "480p", prompt: "" } },
});
const imageResolutions = ["1k", "2k", "4k"];
const videoResolutions = ["480p", "720p", "1080p"];
const imageResolutionOptions: CreativeSelectOption[] = imageResolutions.map((value) => ({
  value,
  label: value.toUpperCase(),
}));
const videoResolutionOptions: CreativeSelectOption[] = videoResolutions.map((value) => ({
  value,
  label: value.toUpperCase(),
}));

watch(
  () => [props.modelValue, props.configs, props.options.models] as const,
  () => {
    if (!props.modelValue) return;
    for (const field of fields.value) {
      const config = props.configs.find((item) => item.model_type === field.type || item.purpose === field.purpose);
      form[field.purpose].model_id = config?.model_id || modelsFor(field)[0]?.value || "";
      Object.assign(form[field.purpose].settings, config?.settings || {});
    }
    // 宽高比是图片接口的固定实现参数，当前统一使用 16:9。
    form.image_generation.settings.aspect_ratio = "16:9";
    form.video_generation.settings.aspect_ratio = "16:9";
    // 视频分辨率按模型配置，缺省时保持原有的 480p 默认值。
    if (!form.video_generation.settings.resolution) form.video_generation.settings.resolution = "480p";
  },
  { immediate: true },
);

/** 获取指定业务用途可选择的模型，并转换为下拉框选项。 */
function modelsFor(field: { purpose: Purpose; type: string }): CreativeSelectOption[] {
  return props.options.models
    .filter(
      (model) =>
        model.model_type === field.type &&
        (field.purpose !== "prompt_reverse" || props.projectType !== "video_redraw" || model.multimodal),
    )
    .map((model) => ({ label: `【${model.channel_name}】${model.name}`, value: model.id }));
}
/** 计算指定业务用途当前模型的价格展示文案。 */
function price(purpose: Purpose) {
  const key = purpose === "prompt_reverse" ? "default" : form[purpose].settings.resolution;
  const model = props.options.models.find((item) => item.id === form[purpose].model_id);
  return modelPriceLabel(model, props.options.prices || [], String(key));
}
/** 按现有事件契约提交全部可见模型配置。 */
function submit() {
  emit(
    "save",
    Object.fromEntries(
      fields.value.map((field) => [field.purpose, JSON.parse(JSON.stringify(form[field.purpose]))]),
    ) as Record<Purpose, Value>,
  );
}

const promptDialog = ref(false);
const promptType = ref("");
const promptLabel = ref("");
const promptPurpose = ref<Purpose>("image_generation");
const selectedPromptId = ref("");
const editing = ref(false);
const creating = ref(false);
const draft = reactive({ id: "", name: "", type: "", content: "" });
const promptTypes = [
  { value: "剧本反推", label: "剧本反推" },
  { value: "剧本分析", label: "剧本分析" },
  { value: "角色生成", label: "角色生成" },
  { value: "场景生成", label: "场景生成" },
  { value: "道具生成", label: "道具生成" },
  { value: "分镜图生成", label: "分镜图生成" },
  { value: "首尾帧生成", label: "首尾帧生成" },
  { value: "视频生成", label: "视频生成" },
];
const promptList = computed(() => props.prompts.filter((item) => item.type === promptType.value));
/** 打开指定用途的提示词面板，并载入当前选择或首个可用预设。 */
function openPrompt(purpose: Purpose, type: string, label: string) {
  promptPurpose.value = purpose;
  promptType.value = type;
  promptLabel.value = label;
  const id = props.promptSelections[type] || "";
  selectedPromptId.value = id;
  const selected = promptList.value.find((item) => item.id === id) || promptList.value[0];
  // 打开面板时只预览当前或默认项，用户明确选择列表项后才持久化选择。
  if (selected) Object.assign(draft, selected);
  else Object.assign(draft, { id: "", name: "", type, content: form[purpose].settings.prompt || "" });
  if (id && selected) form[purpose].settings.prompt = selected.content;
  editing.value = false;
  creating.value = false;
  promptDialog.value = true;
}
/** 选择并应用提示词，同时通知父组件持久化选择。 */
function selectPrompt(prompt: PromptPreset) {
  selectedPromptId.value = prompt.id;
  Object.assign(draft, prompt);
  if (prompt.id) form[promptPurpose.value].settings.prompt = prompt.content;
  if (prompt.id)
    emit("prompt-select", {
      purpose: promptPurpose.value,
      type: promptType.value,
      promptId: prompt.id,
      content: prompt.content,
    });
}
/** 切换提示词启用状态，并保持原有选择事件结构。 */
function togglePrompt(prompt: PromptPreset, enabled: boolean) {
  selectedPromptId.value = enabled ? prompt.id : "";
  if (enabled) {
    Object.assign(draft, prompt);
    form[promptPurpose.value].settings.prompt = prompt.content;
  }
  emit("prompt-select", {
    purpose: promptPurpose.value,
    type: promptType.value,
    promptId: enabled ? prompt.id : "",
    content: enabled ? prompt.content : "",
  });
}
/** 进入自定义提示词新建状态。 */
function startCreate() {
  creating.value = true;
  editing.value = false;
  Object.assign(draft, { id: "", name: "", type: promptType.value, content: "" });
}
/** 在提示词允许编辑时进入编辑状态。 */
function startEdit() {
  if (!draft.id || props.prompts.find((p) => p.id === draft.id)?.editable === false) return;
  editing.value = true;
  creating.value = false;
}
/** 取消编辑并恢复当前已选择的提示词内容。 */
function cancelEdit() {
  creating.value = false;
  editing.value = false;
  const p = promptList.value.find((item) => item.id === selectedPromptId.value);
  if (p) Object.assign(draft, p);
}
/** 校验并提交提示词的新建或更新事件。 */
function savePrompt() {
  const name = draft.name.trim(),
    content = draft.content.trim();
  if (!name || !content) return;
  if (creating.value) emit("prompt-create", { name, type: promptType.value, content });
  else if (draft.id) emit("prompt-update", { id: draft.id, name, type: draft.type || promptType.value, content });
  form[promptPurpose.value].settings.prompt = content;
  creating.value = false;
  editing.value = false;
}
/** 在提示词允许删除时通知父组件执行删除。 */
function deletePrompt() {
  const p = promptList.value.find((item) => item.id === draft.id);
  if (p?.editable !== false) emit("prompt-delete", p);
}
/** 根据业务用途和项目类型生成可配置的提示词入口。 */
const promptButtons = (purpose: Purpose) =>
  purpose === "prompt_reverse"
    ? props.projectType === "video_redraw"
      ? [{ type: "剧本反推", label: "剧本反推提示词" }]
      : props.projectType === "script_analysis"
        ? [{ type: "剧本分析", label: "模型分析提示词" }]
        : []
    : purpose === "image_generation"
      ? [
          { type: "角色生成", label: "角色生图提示词" },
          { type: "场景生成", label: "场景生图提示词" },
          { type: "道具生成", label: "道具生图提示词" },
        ]
      : purpose === "video_generation"
        ? [{ type: "视频生成", label: "视频生成提示词" }]
        : [];
</script>

<template>
  <el-dialog
    :model-value="modelValue"
    title="模型配置"
    width="min(860px, calc(100vw - 28px))"
    class="web-dialog creative-dialog model-config-dialog"
    @close="emit('update:modelValue', false)"
  >
    <div class="model-config-list">
      <section
        v-for="field in fields"
        :key="field.purpose"
        class="model-config-card"
      >
        <header class="model-config-header">
          <strong>{{ field.label }}</strong
          ><span
            v-if="price(field.purpose)"
            class="price"
            >{{ price(field.purpose) }}</span
          >
          <div
            v-if="promptButtons(field.purpose).length"
            class="prompt-buttons"
          >
            <button
              v-for="item in promptButtons(field.purpose)"
              :key="item.type"
              type="button"
              class="prompt-button"
              @click="openPrompt(field.purpose, item.type, item.label)"
            >
              <el-icon> <Setting /> </el-icon>{{ item.label }}
            </button>
          </div>
        </header>
        <div class="model-config-row">
          <div class="model-select">
            <label>模型</label>
            <CreativeSelect
              v-model="form[field.purpose].model_id"
              :options="modelsFor(field)"
              filterable
              placeholder="请选择模型"
            />
          </div>
          <div
            v-if="field.type === 'image'"
            class="resolution-select"
          >
            <label>分辨率</label>
            <CreativeSelect
              v-model="form.image_generation.settings.resolution"
              :options="imageResolutionOptions"
              placeholder="请选择分辨率"
            />
          </div>
          <div
            v-if="field.type === 'video'"
            class="resolution-select"
          >
            <label>分辨率</label>
            <CreativeSelect
              v-model="form.video_generation.settings.resolution"
              :options="videoResolutionOptions"
              placeholder="请选择分辨率"
            />
          </div>
        </div>
      </section>
    </div>
    <template #footer
      ><el-button @click="emit('update:modelValue', false)">取消</el-button
      ><el-button
        type="primary"
        :loading="saving"
        @click="submit"
        >保存配置</el-button
      ></template
    >
  </el-dialog>

  <el-dialog
    v-model="promptDialog"
    :title="promptLabel"
    width="min(900px, calc(100vw - 28px))"
    class="web-dialog creative-dialog prompt-preset-dialog"
  >
    <div class="prompt-layout">
      <aside class="prompt-sidebar">
        <div class="prompt-sidebar-head">
          <span
            ><strong>提示词列表</strong><em>{{ promptList.length }}</em></span
          ><el-button
            size="small"
            :icon="Plus"
            @click="startCreate"
            >新建</el-button
          >
        </div>
        <div class="prompt-list">
          <div
            v-for="item in promptList"
            :key="item.id"
            :class="['prompt-item-row', { active: item.id === selectedPromptId }]"
          >
            <button
              type="button"
              class="prompt-item"
              @click="selectPrompt(item)"
            >
              <span>{{ item.name }}</span
              ><em>{{ item.scope === "user" ? "自定义" : "系统" }}</em></button
            ><el-switch
              :model-value="item.id === selectedPromptId"
              size="small"
              @click.stop
              @change="togglePrompt(item, Boolean($event))"
            />
          </div>
          <p
            v-if="!promptList.length"
            class="prompt-empty"
          >
            暂无提示词
          </p>
        </div>
      </aside>
      <section class="prompt-editor">
        <template v-if="creating || editing"
          ><el-form label-position="top"
            ><el-form-item label="提示词名称"
              ><el-input
                v-model="draft.name"
                placeholder="请输入提示词名称" /></el-form-item
            ><el-form-item
              v-if="editing"
              label="适用类型"
              ><el-select v-model="draft.type"
                ><el-option
                  v-for="item in promptTypes"
                  :key="item.value"
                  :label="item.label"
                  :value="item.value" /></el-select></el-form-item
            ><el-form-item label="提示词内容"
              ><el-input
                v-model="draft.content"
                type="textarea"
                :rows="14"
                resize="none" /></el-form-item
          ></el-form>
          <div class="prompt-actions">
            <el-button @click="cancelEdit">取消</el-button
            ><el-button
              type="primary"
              @click="savePrompt"
              >保存</el-button
            >
          </div> </template
        ><template v-else>
          <header>
            <span>内容预览</span>
            <div>
              <el-button
                v-if="draft.id && props.prompts.find((p) => p.id === draft.id)?.scope === 'user'"
                link
                :icon="Edit"
                @click="startEdit"
                >编辑</el-button
              ><el-button
                v-if="draft.id && props.prompts.find((p) => p.id === draft.id)?.scope === 'user'"
                link
                type="danger"
                :icon="Delete"
                @click="deletePrompt"
                >删除</el-button
              >
            </div>
          </header>
          <el-input
            v-model="draft.content"
            type="textarea"
            :rows="18"
            readonly
            resize="none"
          />
        </template>
      </section>
    </div>
  </el-dialog>
</template>

<style scoped lang="scss">
.model-config-list {
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.model-config-card {
  padding: 18px 20px;
  background: var(--web-surface-soft);
  border: 1px solid var(--web-line-strong);
  border-radius: 10px;
}

.model-config-header {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 14px;
  color: var(--web-text);
}

.model-config-header strong {
  font-size: 15px;
}

.price {
  padding: 4px 8px;
  color: var(--web-accent-dark);
  background: var(--web-hover);
  border: 1px solid var(--web-line-emphasis);
  border-radius: 5px;
  font-size: 12px;
}

.prompt-buttons {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-left: auto;
}

.prompt-button {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 5px 8px;
  color: var(--web-text-secondary);
  background: var(--web-surface);
  border: 1px solid var(--web-line-strong);
  border-radius: 5px;
  font-size: 11px;
  cursor: pointer;
}

.prompt-button:hover {
  color: var(--web-accent-muted);
  border-color: var(--web-switch-border);
}

.model-config-row {
  display: flex;
  align-items: end;
  gap: 12px;
}

.model-select {
  min-width: 0;
  flex: 1;
}

.model-config-row label {
  display: block;
  margin: 0 0 6px;
  color: var(--web-text-subtle);
  font-size: 11px;
}

.resolution-select {
  width: 150px;
  flex: 0 0 150px;
}

.resolution-select :deep(.el-select) {
  width: 100%;
}

.prompt-layout {
  height: min(62vh, 620px);
  display: flex;
  min-height: 390px;
}

.prompt-sidebar {
  width: 270px;
  flex: 0 0 270px;
  border-right: 1px solid var(--web-line-strong);
  overflow: auto;
}

.prompt-sidebar-head {
  min-height: 56px;
  padding: 0 14px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--web-text-secondary);
  border-bottom: 1px solid var(--web-line-strong);
}

.prompt-sidebar-head > span {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.prompt-sidebar-head strong {
  color: var(--web-text);
  font-size: 13px;
  font-weight: 600;
}

.prompt-sidebar-head em {
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  color: var(--web-text-secondary);
  background: var(--web-surface);
  border: 1px solid var(--web-line-strong);
  border-radius: 10px;
  font-size: 11px;
  font-style: normal;
}

.prompt-list {
  padding: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.prompt-item-row {
  min-width: 0;
  padding: 9px 10px;
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--web-surface);
  border: 1px solid var(--web-line-strong);
  border-radius: 8px;
}

.prompt-item-row:hover {
  background: var(--web-surface);
  border-color: var(--web-line-emphasis);
}

.prompt-item-row.active {
  background: var(--web-surface);
  border-color: var(--web-line-active);
}

.prompt-item {
  min-width: 0;
  flex: 1;
  padding: 0;
  display: flex;
  align-items: flex-start;
  flex-direction: column;
  gap: 5px;
  color: var(--web-text-secondary);
  background: transparent;
  border: 0;
  text-align: left;
  cursor: pointer;
}

.prompt-item span {
  width: 100%;
  color: var(--web-text);
  font-size: 13px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.prompt-item em {
  padding: 2px 5px;
  color: var(--web-text-faint);
  background: var(--web-surface-soft);
  border: 1px solid var(--web-line-strong);
  border-radius: 4px;
  font-size: 10px;
  font-style: normal;
  line-height: 1.2;
}

.prompt-item-row.active .prompt-item span {
  color: var(--web-text);
}

.prompt-item-row.active .prompt-item em {
  color: var(--web-accent-dark);
  border-color: var(--web-line-emphasis);
}

.prompt-item-row :deep(.el-switch) {
  flex: 0 0 auto;
}

.prompt-item-row :deep(.el-switch .el-switch__core) {
  background: var(--web-switch-track);
  border-color: var(--web-switch-border);
}

.prompt-item-row :deep(.el-switch.is-checked .el-switch__core) {
  background: var(--web-switch-track-active);
  border-color: var(--web-switch-track-active);
}

.prompt-item-row :deep(.el-switch .el-switch__action) {
  background: var(--web-switch-thumb);
  box-shadow: 0 0 0 1px var(--web-control-outline);
}

.prompt-editor {
  min-width: 0;
  flex: 1;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.prompt-editor > header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  color: var(--web-text-secondary);
  font-size: 12px;
}

.prompt-editor > .el-textarea,
.prompt-editor :deep(.el-textarea) {
  flex: 1;
}

.prompt-editor :deep(.el-textarea__inner) {
  height: 100%;
  background: var(--web-surface-soft);
  border-color: var(--web-line-strong);
  color: var(--web-text);
  resize: none;
  scrollbar-color: var(--web-scrollbar-thumb) var(--web-scrollbar-track);
  scrollbar-width: thin;
}

.prompt-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}

.prompt-empty {
  padding: 20px;
  color: var(--web-empty-text);
  font-size: 12px;
  text-align: center;
}

@media (max-width: 640px) {
  .model-config-row {
    align-items: stretch;
    flex-direction: column;
  }

  .resolution-select {
    width: 100%;
    flex-basis: auto;
  }

  .prompt-buttons {
    width: 100%;
    margin-left: 0;
  }

  .prompt-layout {
    height: 72vh;
    flex-direction: column;
  }

  .prompt-sidebar {
    width: 100%;
    max-height: 190px;
    flex-basis: auto;
    border-right: 0;
    border-bottom: 1px solid var(--web-line-strong);
  }
}
</style>
