<template>
  <el-container class="index gpt">
    <el-aside class="left"
              v-if="!type">
      <div class="chat-list">
        <div class="chat-header">
          <div class="chat-title">大模型AI</div>
          <el-button type="primary"
                     v-if="menu"
                     size="small"
                     @click="box=true">
            <el-icon>
              <Plus />
            </el-icon>配置
          </el-button>
        </div>
        <div class="chat-items">
          <div class="chat-item"
               :class="{'is-active':index==select}"
               @click="handleSelect(index)"
               v-for="(item,index) in list"
               :key="index">
            <el-icon>
              <ChatRound />
            </el-icon>
            <span class="item-name">{{item.name}}</span>
          </div>
        </div>
      </div>
    </el-aside>
    <el-main class="main">
      <el-container class="chat-window">
        <div class="chat-window-header">
          <div class="header-left">
            <span class="current-chat-name">{{obj.name || '新对话'}}</span>
          </div>
          <div class="header-actions">
            <el-select v-model="currentModel"
                       size="small"
                       class="model-select"
                       placeholder="选择模型">
              <el-option v-for="item in modelList"
                         :key="item.value"
                         :label="item.label"
                         :value="item.value">
              </el-option>
            </el-select>
            <el-button-group>
              <el-button size="small"
                         type="primary"
                         @click="clearMessages">
                <el-icon>
                  <Delete />
                </el-icon>
              </el-button>
              <el-button size="small"
                         type="primary"
                         @click="exportMessages">
                <el-icon>
                  <Download />
                </el-icon>
              </el-button>
            </el-button-group>
          </div>
        </div>
        <div class="messages"
             ref="messageContainer">
          <div v-for="(msg, index) in messages"
               :key="index"
               class="message"
               :class="messageClass(msg)">
            <img :src="msg.avatar"
                 class="avatar" />
            <div class="message-content">
              <div class="message-header">
                <span class="username">{{ msg.username }}</span>
                <span class="timestamp">{{ msg.time }}</span>
              </div>
              <div class="message-bubble">
                <el-icon class="message-copy"
                         @click="handleCopy(msg.text)">
                  <DocumentCopy />
                </el-icon>
                <template v-if="loading && messages.indexOf(msg) === messages.length - 1">
                  <div class="typing-indicator">
                    <div class="dot"></div>
                    <div class="dot"></div>
                    <div class="dot"></div>
                  </div>
                </template>
                <div v-else v-html="parseMarkdown(msg.text)"
                     class="markdown-body"></div>
              </div>
            </div>
          </div>
        </div>
        <div class="quick-prompts"
             v-if="obj.exam && obj.exam.length">
          <div class="prompt-title">快捷提示:</div>
          <div class="prompt-list">
            <template v-for="(item,index) in obj.exam">
              <el-tag :key="index"
                      v-if="item"
                      @click="sendMessage(item)"
                      class="prompt-item">
                {{ item }}
              </el-tag>
            </template>

          </div>
        </div>
        <div class="input-area">
          <el-input type="textarea"
                    v-model="newMessage"
                    :rows="3"
                    :disabled="loading"
                    @keyup.enter="sendMessage()"
                    :placeholder="placeholder"
                    resize="none" />
          <el-button type="primary"
                     :disabled="loading"
                     @click="sendMessage()"
                     class="send-button">
            <el-icon>
              <Position />
            </el-icon>
            发送
          </el-button>
        </div>
      </el-container>
    </el-main>

    <el-drawer title="AI配置"
               v-model="box"
               class="avue-dialog"
               size="50%">
      <avue-crud ref="crud"
                 :option="option"
                 v-model:page="page"
                 v-model="form"
                 @row-save="rowSave"
                 @row-update="rowUpdate"
                 @row-del="rowDel"
                 @on-load="onLoad"
                 v-loading="loading"
                 v-bind="$loadingParams"
                 :data="data">
        <template #rules-form="{}">
          <monaco-editor v-model="form.rules"
                         height="200"></monaco-editor>
        </template>
      </avue-crud>
    </el-drawer>
  </el-container>

</template>

<script>
import dayjs from 'dayjs'
import store2 from 'store2'
import { marked } from 'marked'
import MonacoEditor from '@/page/components/monaco-editor'
import { Plus, ChatRound, Delete, Download, DocumentCopy, Position } from '@element-plus/icons-vue'
const format = 'YYYY-MM-DD HH:mm:ss'
import { getList, add, update, remove, getModelList, sendMessage } from "@/api/ai";

export default {
  components: {
    MonacoEditor,
    Plus,
    ChatRound,
    Delete,
    Download,
    DocumentCopy,
    Position
  },
  props: {
    type: [Number, String],
    menu: {
      type: Boolean,
      default: true
    },
    promptOption: Object
  },
  data () {
    return {
      form: {},
      data: [],
      box: false,
      page: {
        pageSize: 10,
        currentPage: 1,
        total: 0
      },
      option: {
        height: 'auto',
        calcHeight: 40,
        index: true,
        labelWidth: 110,
        column: [{
          label: '名称',
          prop: 'name',
          rules: [{
            required: true,
            message: "请输入名称",
            trigger: "blur"
          }]
        }, {
          label: '使用关键字',
          prop: 'idx'
        }, {
          label: '输入提示',
          prop: 'placeholder',
          span: 24,
          hide: true
        }, {
          label: '例子',
          prop: 'exam',
          span: 24,
          hide: true,
          type: 'array',
          separator: '|',
          dataType: 'string'
        }, {
          label: '规则',
          prop: 'rules',
          type: "textarea",
          span: 24,
          hide: true,
        }]
      },
      optionMessage: '',
      select: '',
      loading: false,
      list: [],
      messages: [],
      newMessage: '',
      currentUser: 'avue-data',
      currentUserAvatar: 'https://avuejs.com/images/logo.png',
      modelList: [],
      currentModel: 'deepseek-ai/DeepSeek-V3',
    };
  },
  computed: {
    placeholder () {
      return this.obj.placeholder
    },
    storeName () {
      return 'chatgpt' + this.obj.idx
    },
    obj () {
      return this.list[this.select] || {}
    }
  },
  created () {
    this.getList(() => {
      if (!this.validatenull(this.type)) {
        this.select = this.list.findIndex(ele => ele.idx === this.type);
      } else {
        this.select = 0;
        this.messages = store2.get(this.storeName) || []
      }
      this.scrollToBottom();

      this.optionMessage = this.parseOption()
    })
    this.getModeList()
  },
  methods: {
    getList (callback) {
      getList({
        current: 1,
        size: 99
      }).then(res => {
        let data = res.data.data.records
        data.forEach(ele => {
          ele.exam = ele.exam.split('|')
          ele.rules = ele.rules
        })
        this.list = data;
        callback && callback()
      })
    },
    rowSave (row, done, loading) {
      add(row).then(() => {
        this.onLoad();
        this.$message({
          type: "success",
          message: "操作成功!"
        });
        done();
      }).catch(err => {
        loading()
      });
    },
    rowUpdate (row, index, done, loading) {
      if (this.validData(index) && this.$website.isDemo) {
        this.$message.error(this.$website.isDemoTip)
        done();
        return false;
      }
      update(row).then(() => {
        done();
        this.onLoad();
        this.$message({
          type: "success",
          message: "操作成功!"
        });
      }).catch(err => {
        loading()
      })
    },
    rowDel (row, index) {
      if (this.validData(index) && this.$website.isDemo) {
        this.$message.error(this.$website.isDemoTip)
        return false;
      }
      this.$confirm("确定将选择数据删除?", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning"
      })
        .then(() => {
          return remove(row.id);
        })
        .then(() => {
          this.onLoad();
          this.$message({
            type: "success",
            message: "操作成功!"
          });
        });

    },
    handleSelect (index) {
      this.select = index
      this.messages = store2.get(this.storeName) || []
      this.scrollToBottom();
    },
    onLoad () {
      this.loading = true
      this.getList()
      getList({
        current: this.page.currentPage,
        size: this.page.pageSize,
      }).then(res => {
        this.loading = false
        const data = res.data.data;
        let records = data.records
        this.page.total = data.total;
        this.data = records;
      });
    },
    getModeList () {
      getModelList()
        .then(res => {
          // 处理返回的模型数据
          this.modelList = res.data.data.map(item => ({
            value: item.id,
            label: item.id
          }));
        })
        .catch(err => {
          console.error('获取模型列表失败:', err);
          this.$message.error('获取模型列表失败');
        });
    },
    extractCode (markdown) {
      const codeBlockRegex = /```[\s\S]*?```/g;
      const matches = markdown.match(codeBlockRegex);

      if (!matches) return '';

      return matches.map(block => {
        return block.replace(/```.*\n?|\n?```/g, '').trim();
      }).join('\n\n');
    },
    getMessage (msg, callback, doneFn) {
      this.$emit('start')
      let messageList = this.deepClone(this.messages)
      messageList.forEach(ele => {
        ele.role = ele.username == this.currentUser ? 'user' : 'assistant';
        ele.content = ele.text
      })
      messageList[messageList.length - 2].content = messageList[messageList.length - 2].content + this.optionMessage;
      messageList[messageList.length - 2].content = messageList[messageList.length - 2].content + '&&请严格遵循以下要求：' + this.obj.rules

      let fullResponse = '';
      const decoder = new TextDecoder();
      let buffer = '';

      sendMessage(messageList, this.currentModel)
        .then(async response => {
          this.loading = false;
          if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
          }

          const reader = response.body.getReader();

          while (true) {
            const { value, done } = await reader.read();
            if (done) {
              doneFn && doneFn();
              const codeOnly = this.extractCode(fullResponse);
              this.$emit('code', codeOnly);
              break;
            }

            const chunk = decoder.decode(value, { stream: true });
            buffer += chunk;

            const lines = buffer.split('\n');
            buffer = lines.pop() || '';

            for (const line of lines) {
              const trimmedLine = line.trim();
              if (trimmedLine.startsWith('data: ')) {
                try {
                  const data = JSON.parse(trimmedLine.slice(5));
                  if (data.choices?.[0]?.delta?.content) {
                    fullResponse += data.choices[0].delta.content;
                    callback(fullResponse);
                  }
                } catch (error) {
                  console.warn('Error parsing JSON:', trimmedLine, error);
                }
              }
            }
          }
        })
        .catch(err => {
          console.error('Error in stream:', err);
          this.loading = false;
          this.$message.error('请求失败，请重试');
        })
        .finally(() => {
          this.loading = false;
        });
    },
    handleCopy (data) {
      this.$Clipboard({
        text: data
      }).then(() => {
        this.$message.success('复制成功')
      }).catch(() => {
        this.$message.error('复制失败')
      });
    },
    sendMessage (msg, type) {
      if (msg) this.newMessage = msg;
      if (this.newMessage === '') return;
      if (type) {
        this.messages = []
        this.select = this.list.findIndex(ele => ele.idx === type);
      }
      this.loading = true
      // 添加用户消息
      this.messages.push({
        username: this.currentUser,
        text: this.newMessage,
        time: dayjs().format(format),
        avatar: this.currentUserAvatar,
      });// 预先添加AI消息框
      const aiMessage = {
        username: 'AI',
        text: '',
        time: dayjs().format(format),
        avatar: '/img/ai.svg'
      }
      this.messages.push(aiMessage);

      this.getMessage(this.newMessage, (msg) => {
        // 直接更新已存在的AI消息框的内容
        this.messages[this.messages.length - 1].text = msg;

        this.$nextTick(() => {
          this.scrollToBottom();
        });
      }, () => {
        this.loading = false;

        if (this.validatenull(this.type)) {
          store2.set(this.storeName, this.messages)
        }
      });

      this.newMessage = '';
      this.scrollToBottom();
    },
    parseOption () {
      let type = '';
      if (this.promptOption) {
        type = this.promptOption.type
        this.select = this.list.findIndex(ele => ele.idx === type);
        if (type == 'filter') {
          const { examData, data } = this.promptOption
          const result = `data:${JSON.stringify(data)},result:${JSON.stringify(examData)}`
          return result
        } else {
          const result = `code:${this.promptOption.code}`
          return result
        }
      }

      return ''
    },
    scrollToBottom () {
      this.$nextTick(() => {
        const messageContainer = this.$refs.messageContainer;
        if (messageContainer) messageContainer.scrollTop = messageContainer.scrollHeight;
      });
    },
    parseMarkdown (text) {
      const rawHtml = marked(text);
      return rawHtml;
    },
    // 消息样式类
    messageClass (msg) {
      return {
        'message-right': msg.username === this.currentUser,
        'message-left': msg.username !== this.currentUser
      }
    },
    // 清空消息
    clearMessages () {
      this.$confirm('确定要清空所有消息吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        this.messages = [];
        if (this.validatenull(this.type)) {
          store2.set(this.storeName, []);
        }
        this.$message.success('消息已清空');
      });
    },
    // 导出消息记录
    exportMessages () {
      const content = this.messages.map(msg => {
        return `${msg.username} (${msg.time}):\n${msg.text}\n`;
      }).join('\n');

      const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
      const url = URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = `chat-history-${dayjs().format('YYYY-MM-DD-HH-mm')}.txt`;
      link.click();
      URL.revokeObjectURL(url);
    }
  },
};
</script>

<style lang="scss" scoped>
.gpt {
  min-width: 550px;
  min-height: 400px;
  height: 100%;
  background: var(--bg-color-primary);
  overflow-x: hidden;

  .left {
    width: 260px;
    border-right: 1px solid var(--border-color-base);
    background: var(--bg-color-secondary);
    flex-shrink: 0;
  }

  .main {
    overflow-x: hidden;
    padding: 0;
  }
}

.chat-header {
  padding: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--border-color-base);
}

.chat-title {
  font-size: 18px;
  background-image: var(--gradient-primary);
  -webkit-background-clip: text;
  -webkit-text-fill-color: transparent;
  font-weight: bold;
}

.chat-items {
  padding: 8px;
  overflow-y: auto;
  height: calc(100% - 90px);
}

.chat-item {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 4px 0;
  padding: 10px;
  border: 1px solid var(--border-color-base);
  border-radius: 6px;
  cursor: pointer;
  transition: all 0.3s;

  &:hover {
    background: var(--bg-color-hover);
  }

  &.is-active {
    background: var(--primary-color);
    border-color: var(--primary-color);
  }

  .item-name {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 14px;
  }
}

.chat-window {
  height: 100%;
  display: flex;
  flex-direction: column;
  background: var(--bg-color-secondary);
  border-radius: 8px;
  overflow: hidden;
}

.chat-window-header {
  padding: 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  border-bottom: 1px solid var(--border-color-base);

  .header-left {
    display: flex;
    align-items: center;
    gap: 16px;
    flex: 1;
  }

  .header-actions {
    display: flex;
    align-items: center;
    gap: 12px;
  }

  .model-select {
    width: 200px;
  }

  .current-chat-name {
    font-size: 16px;
    font-weight: 500;
  }
}

.messages {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  padding: 20px;

  .message {
    margin-bottom: 24px;
    display: flex;
    align-items: flex-start;

    &.message-left {
      justify-content: flex-start;

      .avatar {
        margin-right: 12px;
      }
    }

    &.message-right {
      justify-content: flex-end;

      .avatar {
        order: 2;
        margin-left: 12px;
      }

      .message-content {
        align-items: flex-end;
      }

      .message-bubble {
        background: var(--primary-color);
      }
    }

    .avatar {
      width: 40px;
      height: 40px;
      border-radius: 50%;
      border: 2px solid var(--border-color-base);
      padding: 2px;
      background-color: var(--text-color-primary);
      flex-shrink: 0;
    }

    .message-content {
      max-width: 70%;
      display: flex;
      flex-direction: column;
      overflow: hidden;
    }

    .message-header {
      display: flex;
      align-items: center;
      gap: 8px;
      margin-bottom: 4px;

      .username {
        font-weight: 500;
        color: var(--text-color-primary);
      }

      .timestamp {
        font-size: 12px;
        color: var(--text-color-disabled);
      }
    }
  }
}

.message-bubble {
  position: relative;
  background: var(--bg-color-hover);
  padding: 12px 16px;
  border-radius: 8px;
  word-break: break-word;

  &:hover .message-copy {
    opacity: 1;
  }

  .markdown-body {
    font-size: 14px;
    line-height: 1.6;
    color: var(--text-color-light);

    pre {
      background: var(--bg-color-primary);
      border-radius: 4px;
      padding: 12px;
      margin: 8px 0;
      overflow-x: auto;

      code {
        white-space: pre-wrap;
        word-break: break-word;
      }
    }

    code {
      background: var(--bg-color-tertiary);
      padding: 2px 4px;
      border-radius: 3px;
      font-family: monospace;
    }

    img {
      max-width: 100%;
      height: auto;
      border-radius: 4px;
    }

    table {
      width: 100%;
      border-collapse: collapse;
      margin: 8px 0;

      th,
      td {
        border: 1px solid var(--border-color-light);
        padding: 8px;
      }

      th {
        background: var(--bg-color-hover);
      }
    }

    a {
      color: var(--primary-color);
      text-decoration: none;

      &:hover {
        text-decoration: underline;
      }
    }

    ul,
    ol {
      padding-left: 20px;
      margin: 8px 0;
    }

    blockquote {
      margin: 8px 0;
      padding-left: 12px;
      border-left: 4px solid var(--primary-color);
      color: var(--text-color-light);
    }
  }
}

.message-copy {
  position: absolute;
  right: 8px;
  top: 8px;
  opacity: 0;
  transition: opacity 0.3s;
  cursor: pointer;
  color: var(--text-color-disabled);

  &:hover {
    color: var(--text-color-primary);
  }
}

.quick-prompts {
  padding: 12px 20px;
  border-top: 1px solid var(--border-color-base);

  .prompt-title {
    font-size: 14px;
    margin-bottom: 8px;
    color: var(--text-color-disabled);
  }

  .prompt-list {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;

    .prompt-item {
      cursor: pointer;
      transition: all 0.3s;

      &:hover {
        transform: translateY(-2px);
      }
    }
  }
}

.input-area {
  padding: 20px;
  border-top: 1px solid var(--border-color-base);
  display: flex;
  align-items: flex-start;
  gap: 12px;

  .el-input {
    flex: 1;
  }

  .send-button {
    width: 80px;
    height: 70px;
  }
}

// 自定义滚动条样式
::-webkit-scrollbar {
  width: 6px;
  height: 0;
}

::-webkit-scrollbar-thumb {
  background: var(--text-color-disabled);
  border-radius: 3px;
}

::-webkit-scrollbar-track {
  background: var(--bg-color-primary);
}

.el-icon {
  vertical-align: middle;
}

.message-copy {
  font-size: 16px;
}

.send-button .el-icon {
  margin-right: 4px;
}

.typing-indicator {
  display: flex;
  align-items: center;
  gap: 4px;
  padding: 12px 16px;
  min-width: 60px;

  .dot {
    width: 3px;
    height: 3px;
    background: var(--primary-color);
    border-radius: 50%;
    animation: typing 1s infinite ease-in-out;

    &:nth-child(1) {
      animation-delay: 200ms;
    }

    &:nth-child(2) {
      animation-delay: 300ms;
    }

    &:nth-child(3) {
      animation-delay: 400ms;
    }
  }
}

@keyframes typing {
  0% {
    transform: translateY(0px);
    opacity: 0.7;
  }
  50% {
    transform: translateY(-5px);
    opacity: 1;
  }
  100% {
    transform: translateY(0px);
    opacity: 0.7;
  }
}
</style>