<template>
  <div class="container">
    <div class="chat-container">
      <div class="message-list" v-if="messages.length > 0" ref="messageRef">
        <div
          v-for="(msg, index) in messages"
          :key="index"
          class="message"
          :class="{
            'user-message': msg.role === 'user',
            'ai-message': msg.role === 'assistant',
          }"
        >
          <div class="message-box">
            <div v-if="msg.role === 'assistant'" class="picture"></div>
            <div class="message-container">
              <div v-for="item in msg.list" class="messageContent">
                <div class="content">{{ item.content }}</div>
                <div class="audio" v-if="item.url">
                  <AudioPlayer :url="item.url" />
                </div>
              </div>
            </div>
            <!-- <div v-if="msg.role === 'user'" class="picture"></div> -->
          </div>
        </div>
        <div v-if="isQuery" style="margin-left: 60px">
          {{ queryText }}<el-icon><Loading /></el-icon>
        </div>
      </div>
      <div class="welcome" v-else>
        <div>
          <div class="name">{{ `我是${aiName}助手，很高兴见到你` }}</div>
          <div class="function">我可以帮你合成音色，请把你的任务交给我吧~</div>
        </div>
      </div>
      <div class="input-area">
        <el-input
          v-model="inputMessage"
          type="textarea"
          :autosize="{ minRows: 3, maxRows: 6 }"
          resize="none"
          placeholder="Shift+Enter换行"
          @keyup.native="handleKeyUp"
          @keyup.enter.native="handleEnter"
        >
        </el-input>
        <div class="bottom-box">
          <div class="select-box">
            <el-select v-model="timbreValue" size="small" style="width: 120px">
              <el-option
                v-for="item in timbreOptions"
                :key="item"
                :label="item"
                :value="item"
              />
            </el-select>

            <el-select
              v-if="aiType === 'knowledge'"
              v-model="langValue"
              size="small"
              style="width: 100px; margin-left: 10px"
            >
              <el-option
                v-for="item in langOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>

            <el-select
              v-model="dialectValue"
              :disabled="langValue !== '中文'"
              size="small"
              style="width: 100px; margin-left: 10px"
            >
              <el-option
                v-for="item in dialectOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>
            <el-select
              v-if="aiType === 'knowledge'"
              v-model="toneValue"
              size="small"
              style="width: 100px; margin-left: 10px"
            >
              <el-option
                v-for="item in toneOptions"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              />
            </el-select>

            <el-input-number style="width: 100px; margin-left: 10px" v-if="aiType === 'knowledge'" size="small" v-model="countValue" :min="1" :step="1" step-strictly />
          </div>
          <div class="send-message">
            <el-button
              :icon="Position"
              size="small"
              type="primary"
              circle
              :disabled="inputMessage === ''"
              @click="sendMessage"
            />
          </div>
        </div>
      </div>
    </div>
    <div class="addSound" @click="openDialog">
      <el-icon :size="40"><CirclePlus /></el-icon>
    </div>

    <el-dialog
      v-model="dialogVisible"
      title="新增音色"
      width="500"
      :before-close="handleClose"
    >
      <el-form
        ref="ruleFormRef"
        style="max-width: 600px"
        :model="ruleForm"
        :rules="rules"
        label-width="auto"
      >
        <el-form-item label="名称" prop="name">
          <el-input v-model="ruleForm.name" />
        </el-form-item>
      </el-form>
      <SoundRecording ref="soundRecordingRef" />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="handleClose">取消</el-button>
          <el-button type="primary" @click="submit">提交</el-button>
        </div>
      </template>
    </el-dialog>
  </div>
</template>
<script setup>
import { ref } from "vue";
import { Position, CirclePlus } from "@element-plus/icons-vue";
import SoundRecording from "./soundRecording.vue";
import { ElMessage } from "element-plus";
import { createTimbre, getTts, createCorpus, getSynthesize, createAdvancedCorpus, getAdvancedSynthesize } from "@/api/device/register";
import { ConcurrentRequestController } from "@/utils/requestController";
import AudioPlayer from "./AudioPlayer.vue";

const props = defineProps({
  aiName: {
    type: String,
    default: '语音助手'
  },
  aiType: {
    type: String,
    default: 'basis'
  }
})

const messageRef = ref(null);
const inputMessage = ref("");
const messages = ref([]);

const isQuery = ref(false);
const queryText = ref("正在思考中");
const messageIndex = ref(-1)
const controller = new ConcurrentRequestController(3); // 最大并发数为3
const cancelIndex = ref(0)
const sendMessage = async () => {
  if (inputMessage.value.trim()) {
    isQuery.value = true;
    messages.value.push({ role: "user", list: [{ content: inputMessage.value }] });
    await nextTick();
    messageRef.value.scrollTop = messageRef.value.scrollHeight;
    const str = inputMessage.value;
    inputMessage.value = "";
    if (controller.getStatus().queueLength > 0) {
      controller.cancelAll();
      cancelIndex.value = messageIndex.value
      messages.value[cancelIndex.value] = {
        role: "assistant",
        list: [{ content: '已取消查询' }]
      }
    }
    messageIndex.value += 2
    getMessage(str, messageIndex.value);
  }
};

const getMessage = (str, index) => {
  if (props.aiType === 'basis') {
    basisAiOperate(str, index)
  } else if (props.aiType === 'knowledge') {
    knowledgeAiOperate(str, index)
  }
};

// 基础ai(语音合成)发送消息后的操作
const basisAiOperate = async (str, index) => {
  try {
    const res = await createCorpus({
      convertWords: str,
      dialect: dialectValue.value,
      timbre: timbreValue.value,
    });
    if (res.code === 200) {
      const msg = JSON.parse(res?.msg || "[]");
      if (msg.length === 0) {
        serviceError("未查询到相关服务");
      } else {
        queryText.value = "语音合成中";
        getBasisAudio(msg, index);
      }
    } else {
      serviceError("服务器繁忙，请稍后重试");
    }
  } catch (err) {
    serviceError("服务器繁忙，请稍后重试");
  }
}

const getBasisAudio = async (arr, index) => {
  messages.value[index] = {
    role: "assistant",
    list: []
  }
  await basisApiController(arr, index);
  isQuery.value = false;
};

async function basisApiController(arr, messageIndex) {
  // 模拟10个需要发送的请求
  const requests = arr.map((item, index) => {
    return (signal) =>
      getSynthesize({
        dialect: dialectValue.value,
        text: item,
        timbre: timbreValue.value,
      }, {
        signal: signal
      })
        .then(async (data) => {
          if (cancelIndex.value === messageIndex) {
            return
          }
          console.log(`请求 ${index} 完成`);
          const url = URL.createObjectURL(data);
          messages.value[messageIndex].list.push({
            content: item,
            url,
          })
          await nextTick();
          messageRef.value.scrollTop = messageRef.value.scrollHeight;
          return data;
        })
        .catch(async (error) => {
          if (cancelIndex.value === messageIndex) {
            return
          }
          console.error(`请求 ${index} 失败:`, error);
          messages.value[messageIndex].list.push({
            content: item + " ，语音转换失败"
          })
           await nextTick();
        messageRef.value.scrollTop = messageRef.value.scrollHeight;
          throw error;
        });
  });

  // 发送所有请求
  const results = await Promise.allSettled(
    requests.map((request) => controller.addRequest(request))
  );

  // 处理结果
  const successfulResults = results
    .filter((result) => result.status === "fulfilled")
    .map((result) => result.value);

  console.log("所有请求已完成", successfulResults);
  return successfulResults;
}

// 语音合成(知识库)发送消息后的操作
const knowledgeAiOperate = async (str, index) => {
  try {
    const params = {
      convertWords: str,
      dialect: langValue.value === '中文' ? dialectValue.value : '',
      timbre: timbreValue.value,
      language: langValue.value,
      tone : toneValue.value,
      count: countValue.value
    }
    const res = await createAdvancedCorpus(params);
    if (res.code === 200) {
      const msg = JSON.parse(res?.msg || "[]");
      if (msg.length === 0) {
        serviceError("未查询到相关服务");
      } else {
        queryText.value = "语音合成中";
        getKnowledgeAudio(msg, index, params);
      }
    } else {
      serviceError("服务器繁忙，请稍后重试");
    }
  } catch (err) {
    serviceError("服务器繁忙，请稍后重试");
  }
}

const getKnowledgeAudio = async (arr, index, params) => {
  messages.value[index] = {
    role: "assistant",
    list: []
  }
  const data = await knowledgeApiController(arr, index, params);
  console.log('data', data)
  isQuery.value = false;
}

async function knowledgeApiController(arr, messageIndex, params) {
  // 模拟10个需要发送的请求
  const requests = arr.map((item, index) => {
    return (signal) =>
      getAdvancedSynthesize({
        dialect: params.dialect,
        text: item,
        timbre: params.timbre,
        tone: params.tone,
        language: params.language
      }, {
        signal: signal
      })
        .then(async (data) => {
          if (cancelIndex.value === messageIndex) {
            return
          }
          console.log(`请求 ${index} 完成`);
          const url = URL.createObjectURL(data);
          messages.value[messageIndex].list.push({
            content: item,
            url,
          })
          await nextTick();
          messageRef.value.scrollTop = messageRef.value.scrollHeight;
          return data;
        })
        .catch(async (error) => {
          if (cancelIndex.value === messageIndex) {
            return
          }
          console.error(`请求 ${index} 失败:`, error);
          messages.value[messageIndex].list.push({
            content: item + " ，语音转换失败"
          })
          await nextTick();
          messageRef.value.scrollTop = messageRef.value.scrollHeight;
          throw error;
        });
  });

  // 发送所有请求
  const results = await Promise.allSettled(
    requests.map((request) => controller.addRequest(request))
  );

  // 处理结果
  const successfulResults = results
    .filter((result) => result.status === "fulfilled")
    .map((result) => result.value);

  console.log("所有请求已完成", successfulResults);
  return successfulResults;
}

const serviceError = (content) => {
  isQuery.value = false;
  messages.value.push({
    role: "assistant",
    list: [{content}]
  });
};

const handleKeyUp = (event) => {
  // 阻止shift+enter触发的回车键事件冒泡
  if (event.key === "Enter" && event.shiftKey) {
    event.stopPropagation();
  }
};

const handleEnter = (event) => {
  // 只处理没有按住shift键的回车键事件
  if (!event.shiftKey) {
    sendMessage();
  }
};

const timbreValue = ref("");
const timbreOptions = ref([]);

const dialectValue = ref("普通话");
const dialectOptions = [
  {
    label: "普通话",
    value: "普通话",
  },
  {
    label: "粤语",
    value: "粤语",
  },
  {
    label: "四川话",
    value: "四川话",
  },
  {
    label: "上海话",
    value: "上海话",
  },
  {
    label: "南京话",
    value: "南京话",
  },
  {
    label: "天津话",
    value: "天津话",
  },
  {
    label: "武汉话",
    value: "武汉话",
  },
];

const toneValue = ref('中性')
const toneOptions = [
  {
    label: '中性',
    value: '中性'
  },
  {
    label: "高兴",
    value: "高兴",
  },
  {
    label: "悲伤",
    value: "悲伤",
  },
  {
    label: "愤怒",
    value: "愤怒",
  },
  // {
  //   label: "冷漠",
  //   value: "冷漠",
  // }
]

const langValue = ref('中文')
const langOptions = [
  {
    label: "中文",
    value: "中文",
  },
  {
    label: "英文",
    value: "英文",
  },
  {
    label: "日文",
    value: "日文",
  },
  {
    label: "韩语",
    value: "韩语",
  }
]

const countValue = ref(1)

const getTimbre = async () => {
  const req = await getTts();
  if (req.code === 200) {
    timbreOptions.value = req.data
    timbreValue.value = req.data[0]
  }
};
getTimbre();

const dialogVisible = ref(false);

const openDialog = () => {
  dialogVisible.value = true;
};

const ruleFormRef = ref();
const ruleForm = reactive({
  name: "",
});

const rules = reactive({
  name: [{ required: true, message: "请输入名称", trigger: "blur" }],
});

const soundRecordingRef = ref(null);

const submit = () => {
  ruleFormRef.value.validate(async (valid) => {
    if (valid) {
      const globalBlob = soundRecordingRef.value.getWav();
      // 直接创建新Blob并指定WAV类型
      if (globalBlob) {
        const file = new File([globalBlob], `${ruleForm.name}.m4a`, {
          type: "audio/m4a",
        });
        const formData = new FormData();
        formData.append("file", file);
        formData.append("language_instruction", ruleForm.name);

        const res = await createTimbre(formData);
        if (res.code === 200) {
          ElMessage.success("操作成功");
          handleClose();
          getTimbre();
        } else {
          ElMessage.success(res?.msg || "操作失败");
        }
      } else {
        ElMessage.error("请先录音");
      }
    } else {
      console.log("error submit!");
    }
  });
};

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

const resetForm = () => {
  ruleFormRef.value.resetFields();
  soundRecordingRef.value.reset();
};
</script>

<style lang="scss" scoped>
.container {
  width: 100%;
  display: flex;
  justify-content: center;

  .chat-container {
    display: flex;
    flex-direction: column;
    width: 800px;
    height: 100%;

    .message-list {
      flex: 1;
      overflow-y: auto;
      padding: 20px;
      position: relative;

      .message {
        display: flex;
        overflow-wrap: break-word;
        margin: 10px;
        position: relative;
        align-items: center;
      }

      .message-box {
        display: flex;
        // flex-direction: column;
        // align-items: end;
      }

      .user-message {
        justify-content: right;

        .content {
          border-radius: 8px;
          background-color: #ddd9ff;
          white-space: pre-wrap;
          padding: 10px;
          max-width: 500px;
        }

        .picture {
          width: 40px;
          height: 40px;
          background-image: url("./img/user.png");
          background-repeat: no-repeat;
          background-size: 100% 100%;
          margin-bottom: 10px;
          margin-left: 10px;
        }
      }

      .ai-message {
        .audio {
          margin-left: 20px;

          audio::-webkit-media-controls {
            display: none; /* Chrome, Safari, Opera */
          }
        }

        .message-container {
          background-color: #eff6ff;
          border-radius: 12px;

          .messageContent {
            display: flex;
            align-items: center;
            padding: 10px;
            max-width: 500px;
          }
        }

        .picture {
          width: 40px;
          height: 40px;
          background-image: url("./img/user.png");
          background-repeat: no-repeat;
          background-size: 100% 100%;
          margin-right: 10px;
        }
      }
    }

    .welcome {
      flex: 1;
      display: flex;
      align-items: center;
      justify-content: center;

      .name {
        font-size: 40px;
      }

      .function {
        margin-top: 10px;
        font-size: 20px;
      }
    }

    :deep(.input-area) {
      padding: 20px;
      height: 140px;
      border: 1px solid #ddd9ff;
      border-radius: 20px;

      .el-textarea__inner {
        box-shadow: none;

        &:focus {
          box-shadow: none;
        }

        &:hover {
          box-shadow: none;
        }
      }

      .bottom-box {
        margin-top: 8px;
        display: flex;
        align-items: center;
        justify-content: space-between;

        .el-select__wrapper {
          border-radius: 10px;
        }
      }
    }

    .input-area:has(.el-textarea__inner:focus) {
      box-shadow: 0 0 0 1px #ddd9ff inset;
    }

    .input-area:has(.el-textarea__inner:hover) {
      box-shadow: 0 0 0 1px #ddd9ff inset;
    }
  }

  .addSound {
    margin-left: 8px;
    cursor: pointer;
  }
}
</style>
