<template>
  <div class="conversation-panel">
    <!-- 配置加载中 -->
    <div v-if="configLoading" class="loading-tip">
      <i class="el-icon-loading"></i>
      <p>正在加载配置...</p>
    </div>
    <!-- 配置加载失败 / 无配置 -->
    <div v-else-if="configError" class="empty-tip">
      <i class="el-icon-warning"></i>
      <p>{{ configErrorMsg || '企微未配置，请先配置企微应用' }}</p>
    </div>
    <!-- 未登录 -->
    <div v-else-if="!isLoggedIn" class="login-area">
      <div class="login-tip">请扫码登录企微</div>
      <div id="login-container" ref="loginContainer" class="login-container"></div>
    </div>
    <!-- 已登录但未选择员工（群聊不需要员工） -->
    <div v-else-if="!staffUserId && !chatId" class="empty-tip">
      <i class="el-icon-info"></i>
      <p>请选择员工</p>
    </div>
    <!-- 已登录但未选择客户（群聊不需要客户） -->
    <div v-else-if="!customerId && !chatId" class="empty-tip">
      <i class="el-icon-info"></i>
      <p>请选择客户查看会话</p>
    </div>
    <!-- 会话加载中 -->
    <div v-else-if="!isReady" class="loading-tip">
      <i class="el-icon-loading"></i>
      <p>正在加载会话记录...</p>
    </div>
    <!-- 会话为空 -->
    <div v-else-if="msgList.length === 0" class="empty-tip">
      <i class="el-icon-info"></i>
      <p>暂无会话数据</p>
    </div>
    <!-- 聊天窗口 -->
    <div v-else id="chat-container" ref="chatContainer"></div>
  </div>
</template>

<script>
import * as ww from '@wecom/jssdk';
import { qwLogin, qwSignature, qwConversations, getQwSessionConfig } from '@/api/qw/companySession';

// ========== 全局配置缓存 ==========
window._QW_CONFIG_CACHE = window._QW_CONFIG_CACHE || new Map();
window._QW_CONFIG_PENDING = window._QW_CONFIG_PENDING || new Map();
const CACHE_TTL = 5 * 60 * 1000;

function getCachedConfig(corpId) {
  if (!corpId) return null;
  const cached = window._QW_CONFIG_CACHE.get(corpId);
  if (cached && (Date.now() - cached.timestamp) < CACHE_TTL) {
    return cached.config;
  }
  window._QW_CONFIG_CACHE.delete(corpId);
  return null;
}

function setCachedConfig(corpId, config) {
  if (!corpId) return;
  window._QW_CONFIG_CACHE.set(corpId, { config, timestamp: Date.now() });
}

export default {
  name: 'ConversationPanelPure',
  props: {
    corpId: { type: String, default: null },
    customerId: { type: String, default: null },
    staffUserId: { type: String, default: null },
    chatId: { type: String, default: null }
  },
  data() {
    return {
      isLoggedIn: false,
      isReady: false,
      msgList: [],
      cursor: '',
      hasMore: true,
      loadingMore: false,
      chatInstance: null,
      _sdkInited: false,
      config: { corpid: '', agentid: '', domain: '' },
      configReady: false,
      configError: false,
      configLoading: false,
      configErrorMsg: '',
      tokenCheckTimer: null,
      _hasEmittedLogout: false,
      _loginPanelCreated: false,
      _loginPanelTimer: null
    };
  },
  watch: {
    customerId(newId, oldId) {
      if (newId && newId !== oldId && this.isLoggedIn && (this.staffUserId || this.chatId) && this.configReady && !this.configError) {
        this.resetAndReload();
      }
    },
    staffUserId(newVal, oldVal) {
      if (newVal && newVal !== oldVal && this.customerId && this.isLoggedIn && this.configReady && !this.configError) {
        this.resetAndReload();
      }
    },
    chatId(newVal, oldVal) {
      if (newVal && newVal !== oldVal && this.isLoggedIn && this.configReady && !this.configError) {
        this.resetAndReload();
      }
    },
    corpId(newId, oldId) {
      if (newId !== oldId) {
        this.destroyChat();
        this.isReady = false;
        this._sdkInited = false;
        this._hasEmittedLogout = false;
        this._loginPanelCreated = false;
        if (this._loginPanelTimer) clearTimeout(this._loginPanelTimer);
        this.msgList = [];
        this.cursor = '';
        this.hasMore = true;
        this.loadingMore = false;
        this.isLoggedIn = false;

        const cached = getCachedConfig(newId);
        if (cached) {
          this.config = cached;
          this.configReady = true;
          this.configError = false;
          this.configLoading = false;
          this.handleAuthAndLoad();
        } else {
          this.configReady = false;
          this.configError = false;
          this.configLoading = false;
          this.initConfig().then(() => {
            if (this.configReady && !this.configError) this.handleAuthAndLoad();
          });
        }
      }
    },
    isLoggedIn: {
      handler(newVal) {
        if (!newVal && this.configReady && !this.configError && this._sdkInited && !this._loginPanelCreated) {
          this.tryCreateLoginPanel();
        }
      },
      immediate: true
    }
  },
  async mounted() {
    this._hasEmittedLogout = false;
    this._loginPanelCreated = false;

    const cached = getCachedConfig(this.corpId);
    if (cached) {
      this.config = cached;
      this.configReady = true;
      this.configError = false;
      this.configLoading = false;
      await this.handleAuthAndLoad();
    } else {
      await this.initConfig();
      if (this.configReady && !this.configError) await this.handleAuthAndLoad();
    }

    this.tokenCheckTimer = setInterval(() => {
      if (this.isLoggedIn && this.configReady && !this.configError && this._sdkInited) {
        this.getAgentConfigSignature().catch(e => console.warn('签名刷新失败', e));
      }
    }, 90 * 60 * 1000);
  },
  beforeDestroy() {
    if (this.tokenCheckTimer) clearInterval(this.tokenCheckTimer);
    if (this._loginPanelTimer) clearTimeout(this._loginPanelTimer);
    this.destroyChat();
  },
  methods: {
    // ========== Storage Key ==========
    getStorageKey(corpId, suffix = 'expire') {
      return `wecom_session_${corpId}_${suffix}`;
    },
    checkLoginState(corpId) {
      const expire = localStorage.getItem(this.getStorageKey(corpId, 'expire'));
      return expire && Date.now() < parseInt(expire, 10);
    },
    storeLoginState(corpId) {
      localStorage.setItem(this.getStorageKey(corpId, 'expire'), String(Date.now() + 115 * 60 * 1000));
    },
    clearLoginState(corpId) {
      localStorage.removeItem(this.getStorageKey(corpId, 'expire'));
    },

    // ========== 配置加载 ==========
    async initConfig() {
      if (!this.corpId || this.configReady || this.configError) return;

      const cached = getCachedConfig(this.corpId);
      if (cached) {
        this.config = cached;
        this.configReady = true;
        this.configError = false;
        this.configLoading = false;
        return;
      }

      if (window._QW_CONFIG_PENDING.has(this.corpId)) {
        return window._QW_CONFIG_PENDING.get(this.corpId);
      }

      this.configLoading = true;
      const promise = (async () => {
        try {
          const res = await getQwSessionConfig(this.corpId);
          let configData = null;
          if (res?.code === 200 && res.data?.corpid) configData = res.data;
          else if (res?.data?.corpid) configData = res.data;
          else if (res?.corpid) configData = res;

          if (!configData?.corpid || !configData?.agentid) throw new Error('配置数据不完整');

          const cfg = {
            corpid: configData.corpid,
            agentid: String(configData.agentid),
            domain: configData.domain || ''
          };
          this.config = cfg;
          this.configReady = true;
          this.configError = false;
          setCachedConfig(this.corpId, cfg);
        } catch (e) {
          alert("请更换企微主体！！！")
          console.error('[ConversationPanel] 获取配置失败:', e);
          this.configReady = false;
          this.configError = true;
          this.configErrorMsg = e.message || '获取配置失败';
        } finally {
          this.configLoading = false;
          window._QW_CONFIG_PENDING.delete(this.corpId);
        }
      })();
      window._QW_CONFIG_PENDING.set(this.corpId, promise);
      return promise;
    },

    // ========== 认证与加载 ==========
    async handleAuthAndLoad() {
      if (this.configError) return;
      if (!this.configReady) {
        await this.initConfig();
        if (this.configError) return;
      }
      const sdkOk = await this.initSDKOnce();
      if (!sdkOk) return;

      const urlParams = new URLSearchParams(window.location.search);
      const code = urlParams.get('code');
      if (code) {
        window.history.replaceState({}, '', window.location.origin + window.location.pathname);
        try {
          await this.handleLogin(code);
          this.storeLoginState(this.corpId);
          this.isLoggedIn = true;
          if ((this.customerId && this.staffUserId) || this.chatId) await this.loadFirstPage();
        } catch (err) {
          this.clearLoginState(this.corpId);
          this.isLoggedIn = false;
          this.safeEmitLogout();
        }
      } else {
        if (this.checkLoginState(this.corpId)) {
          this.isLoggedIn = true;
          if ((this.customerId && this.staffUserId) || this.chatId) await this.loadFirstPage();
        } else {
          this.isLoggedIn = false;
          this.safeEmitLogout();
          this.tryCreateLoginPanel();
        }
      }
    },

    tryCreateLoginPanel() {
      if (this._loginPanelCreated || !this.configReady || this.configError || !this._sdkInited || this.isLoggedIn) return;
      if (this._loginPanelTimer) clearTimeout(this._loginPanelTimer);
      this.$nextTick(() => this.createLoginPanel());
    },

    createLoginPanel() {
      if (this._loginPanelCreated || !this.configReady || this.configError || !this._sdkInited || this.isLoggedIn) return;
      let container = this.$refs.loginContainer || document.getElementById('login-container');
      if (!container) {
        this._loginPanelTimer = setTimeout(() => this.createLoginPanel(), 1000);
        return;
      }
      if (container.getBoundingClientRect().width === 0 || container.getBoundingClientRect().height === 0) {
        this._loginPanelTimer = setTimeout(() => this.createLoginPanel(), 500);
        return;
      }
      container.innerHTML = '';
      this._loginPanelCreated = true;
      const redirectUri = window.location.origin + window.location.pathname;
      try {
        ww.createWWLoginPanel({
          el: container,
          params: {
            login_type: 'CorpApp',
            appid: this.config.corpid,
            agentid: this.config.agentid,
            redirect_uri: redirectUri,
            redirect_type: 'callback',
            state: 'state_' + Date.now()
          },
          onLoginSuccess: async ({ code }) => {
            try {
              await this.handleLogin(code);
              if ((this.customerId && this.staffUserId) || this.chatId) await this.loadFirstPage();
            } catch (e) {
              this.clearLoginState(this.corpId);
              this.isLoggedIn = false;
              this._loginPanelCreated = false;
              this.safeEmitLogout();
            }
          },
          onLoginError: () => {
            this._loginPanelCreated = false;
            this._loginPanelTimer = setTimeout(() => this.createLoginPanel(), 3000);
          }
        });
      } catch (e) {
        this._loginPanelCreated = false;
        this._loginPanelTimer = setTimeout(() => this.createLoginPanel(), 3000);
      }
    },

    safeEmitLogout() {
      if (this._hasEmittedLogout) return;
      this._hasEmittedLogout = true;
      this.$emit('logout');
    },

    async handleLogin(code) {
      const res = await qwLogin({ code, corpid: this.corpId });
      const data = this._extractResponse(res);
      if (data.errcode !== 0) throw new Error(data.errmsg || '登录失败');
      this.storeLoginState(this.corpId);
      this.isLoggedIn = true;
      this.$emit('login-success');
    },

    async getAgentConfigSignature() {
      if (!this.configReady || this.configError) throw new Error('配置未就绪');
      const currentUrl = window.location.href.split('#')[0];
      const res = await qwSignature({ url: currentUrl, corpid: this.corpId });
      const data = this._extractResponse(res);
      if (data.errcode && data.errcode !== 0) {
        this.clearLoginState(this.corpId);
        this.isLoggedIn = false;
        this.isReady = false;
        this.destroyChat();
        this._loginPanelCreated = false;
        if (!this.configError) this.safeEmitLogout();
        throw new Error(`签名失败: ${data.errmsg}`);
      }
      return { timestamp: data.timestamp, nonceStr: data.nonceStr, signature: data.signature };
    },

    async initSDKOnce() {
      if (this._sdkInited) return true;
      if (!this.configReady || this.configError) return false;
      try {
        await ww.register({
          corpId: this.config.corpid,
          agentId: this.config.agentid,
          jsApiList: ['selectExternalContact', 'shareAppMessage', 'wwapp.invokeJsApiByCallInfo'],
          getAgentConfigSignature: () => this.getAgentConfigSignature()
        });
        await ww.initOpenData();
        this._sdkInited = true;
        return true;
      } catch (e) {
        console.error('SDK初始化失败', e);
        return false;
      }
    },

    async loadFirstPage() {
      if (this.configError) return;
      try {
        const res = await qwConversations({
          customerId: this.customerId,
          staffUserId: this.staffUserId,
          limit: 100,
          cursor: this.cursor,
          corpid: this.corpId,
          chatId: this.chatId || undefined
        });
        const data = this._extractResponse(res);
        if (data.errcode !== 0) throw new Error(data.errmsg || '拉取失败');
        const rawList = data.data || [];
        this.msgList = this.processMessages(rawList, false);
        this.cursor = data.next_cursor || '';
        this.hasMore = data.has_more === 1;
        this.isReady = true;
        await this.$nextTick();
        if (this.msgList.length) await this.renderOrUpdateChat();
      } catch (e) {
        console.error('加载第一页失败', e);
        this.$message.error('加载会话失败：' + e.message);
        this.isReady = true;
        this.msgList = [];
      }
    },

    async loadMore() {
      if (!this.cursor || this.loadingMore || !this.hasMore || this.configError) return;
      this.loadingMore = true;
      try {
        const res = await qwConversations({
          customerId: this.customerId,
          staffUserId: this.staffUserId,
          limit: 50,
          cursor: this.cursor,
          corpid: this.corpId,
          chatId: this.chatId || undefined
        });
        const data = this._extractResponse(res);
        if (data.errcode !== 0) throw new Error(data.errmsg || '拉取更多失败');
        const newMessages = data.data || [];
        if (newMessages.length) {
          const processed = this.processMessages(newMessages, true);
          this.msgList.push(...processed);
          this.cursor = data.next_cursor || '';
          this.hasMore = data.has_more === 1;
          if (this.chatInstance) this.chatInstance.setData({ msgList: this.msgList });
          else await this.renderOrUpdateChat();
        } else {
          this.hasMore = false;
        }
      } catch (e) {
        console.error('加载更多失败', e);
      } finally {
        this.loadingMore = false;
      }
    },

    processMessages(messages, isAppend = false) {
      const prevLastMsg = isAppend && this.msgList.length > 0
        ? this.msgList[this.msgList.length - 1]
        : null
      let prevDate = prevLastMsg ? (prevLastMsg.dateStr || '') : ''

      return messages.map(msg => {
        const timeStr = msg.send_time_str || ''
        const parts = timeStr.split(' ')
        const dateStr = parts[0] || ''
        const displayTime = parts[1] ? parts[1].substring(0, 5) : ''
        const showDate = !!dateStr && dateStr !== prevDate
        prevDate = dateStr
        return {
          ...msg,
          dateStr,
          displayTime,
          showDate
        }
      })
    },
    async renderOrUpdateChat() {
      if (this.msgList.length === 0 || this.configError) return;
      const container = document.getElementById('chat-container');
      if (!container) return;
      if (this.chatInstance) {
        this.chatInstance.setData({ msgList: this.msgList });
        return;
      }
      const ok = await this.initSDKOnce();
      if (!ok) return;
      const factory = ww.createOpenDataFrameFactory();
      if (!factory) return;

      this.chatInstance = factory.createOpenDataFrame({
        el: container,
        template: `
          <scroll-view scroll-y="{{true}}" bindscrolltolower="onScrollToLower" style="height: 100%;">
            <view wx:for="{{data.msgList}}" wx:for-item="msg" wx:key="msgid" style="margin-bottom: 15px;">
              <!-- 日期分隔线：仅跨天时显示 -->
              <view wx:if="{{msg.showDate}}" style="text-align: center; margin-bottom: 12px;">
                <span style="background:#e9e9e9; color:#888; padding:3px 12px; border-radius:12px; font-size:11px;">{{ msg.dateStr }}</span>
              </view>
              <!-- 客户/机器人消息（左对齐） -->
              <view wx:if="{{msg.sender.type == 2 || msg.sender.type == 3}}" style="display:flex; flex-direction:row; align-items:flex-start;">
                <ww-open-data wx:if="{{msg.sender.type == 2}}" type="externalUserAvatar" openid="{{msg.sender.id}}" class="msg-avatar-left" />
                <ww-open-data wx:if="{{msg.sender.type == 3}}" type="userAvatar" openid="{{msg.sender.id}}" class="msg-avatar-left" />
                <view style="display:flex; flex-direction:column; max-width:70%; margin-left:10px;">
                  <view style="display:flex; align-items:center; margin-bottom:4px;">
                    <ww-open-data wx:if="{{msg.sender.type == 2}}" type="externalUserName" openid="{{msg.sender.id}}" class="sender-name" />
                    <ww-open-data wx:if="{{msg.sender.type == 3}}" type="userName" openid="{{msg.sender.id}}" class="sender-name" />
                    <span style="font-size:11px;color:#999;margin-left:8px;">{{ msg.displayTime }}</span>
                  </view>
                  <view class="msg-bubble msg-bubble-left">
                    <ww-open-message message-id="{{msg.msgid}}" secret-key="{{msg.secretKey}}" open-type="viewMessage"/>
                  </view>
                </view>
              </view>
              <!-- 员工消息（右对齐） -->
              <view wx:if="{{msg.sender.type == 1}}" style="display:flex; flex-direction:row; justify-content:flex-end; align-items:flex-start;">
                <view style="display:flex; flex-direction:column; align-items:flex-end; max-width:70%; margin-right:10px;">
                  <view style="display:flex; align-items:center; margin-bottom:4px;">
                    <span style="font-size:11px;color:#999;margin-right:8px;">{{ msg.displayTime }}</span>
                    <ww-open-data type="userName" openid="{{msg.sender.id}}" class="sender-name" />
                  </view>
                  <view class="msg-bubble msg-bubble-right">
                    <ww-open-message message-id="{{msg.msgid}}" secret-key="{{msg.secretKey}}" open-type="viewMessage"/>
                  </view>
                </view>
                <ww-open-data type="userAvatar" openid="{{msg.sender.id}}" class="msg-avatar-right" />
              </view>
              <!-- 未知类型消息（居中，无头像） -->
              <view wx:if="{{msg.sender.type != 1 && msg.sender.type != 2 && msg.sender.type != 3}}" style="display:flex; justify-content:center;">
                <view class="msg-bubble msg-bubble-left">
                  <ww-open-message message-id="{{msg.msgid}}" secret-key="{{msg.secretKey}}" open-type="viewMessage"/>
                </view>
              </view>
            </view>
            <view wx:if="{{data.hasMore && data.loadingMore}}" style="text-align:center; padding:10px; color:#999;">加载更多...</view>
          </scroll-view>
        `,
        style: `
          .msg-avatar-left {
            width: 36px;
            height: 36px;
            border-radius: 4px;
            flex-shrink: 0;
          }
          .msg-avatar-right {
            width: 36px;
            height: 36px;
            border-radius: 4px;
            flex-shrink: 0;
          }
          .sender-name {
            font-size: 12px;
            color: #666;
            max-width: 120px;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
          }
          .msg-bubble {
            padding: 10px 14px;
            border-radius: 8px;
            word-break: break-all;
            display: flex;
            align-items: center;
          }
          .msg-bubble-left {
            background: #ffffff;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
          }
          .msg-bubble-right {
            background: #95ec69;
            box-shadow: 0 1px 3px rgba(0,0,0,0.1);
          }
        `,
        data: {
          msgList: this.msgList,
          hasMore: this.hasMore,
          loadingMore: this.loadingMore
        },
        methods: {
          onScrollToLower: () => {
            if (this.hasMore && !this.loadingMore && !this.configError) this.loadMore();
          }
        },
        error: (e) => {
          console.error('[企微组件] 错误', e);
          if (e && [42006, 42003, 40029].includes(e.errCode)) {
            if (!this.configError) {
              this.clearLoginState(this.corpId);
              this.isLoggedIn = false;
              this.isReady = false;
              this.destroyChat();
              this._loginPanelCreated = false;
              this.safeEmitLogout();
            }
          }
        },
        handleModal: ({ modalUrl, modalSize }) => {
          const mask = document.createElement('div');
          mask.style.cssText = 'position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);z-index:9999;display:flex;align-items:center;justify-content:center;';
          const content = document.createElement('div');
          content.style.cssText = `position:relative;max-width:90vw;max-height:90vh;width:${modalSize?.width || 80}%;height:${modalSize?.height || 80}%;background:#fff;border-radius:8px;overflow:hidden;`;
          const closeBtn = document.createElement('button');
          closeBtn.innerText = '✕';
          closeBtn.style.cssText = 'position:absolute;top:10px;right:10px;z-index:10001;width:32px;height:32px;border-radius:50%;border:none;background:rgba(0,0,0,0.5);color:#fff;font-size:20px;cursor:pointer;display:flex;align-items:center;justify-content:center;';
          const iframe = document.createElement('iframe');
          iframe.src = modalUrl;
          iframe.style.cssText = 'width:100%;height:100%;border:none;';
          const cleanup = () => {
            mask.remove();
            document.removeEventListener('keydown', escHandler);
          };
          closeBtn.onclick = (e) => { e.stopPropagation(); cleanup(); };
          mask.onclick = (e) => { if (e.target === mask) cleanup(); };
          const escHandler = (e) => { if (e.key === 'Escape') cleanup(); };
          document.addEventListener('keydown', escHandler);
          content.appendChild(iframe);
          content.appendChild(closeBtn);
          mask.appendChild(content);
          document.body.appendChild(mask);
          return true;
        }
      });
    },

    async resetAndReload() {
      this.destroyChat();
      this.msgList = [];
      this.cursor = '';
      this.hasMore = true;
      this.loadingMore = false;
      this.isReady = false;
      await this.loadFirstPage();
    },

    destroyChat() {
      if (this.chatInstance) this.chatInstance = null;
      const container = document.getElementById('chat-container');
      if (container) container.innerHTML = '';
    },

    _extractResponse(res) {
      if (!res) return {};
      if (res.code !== undefined) return res;
      if (res.errcode !== undefined || res.timestamp || res.corpid || res.data) return res;
      if (res.data?.data) return res.data.data;
      return res;
    }
  }
};
</script>

<style scoped>
.conversation-panel {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  background: #f5f6f7;
  position: relative;
}
.login-area, .empty-tip, .loading-tip {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  height: 100%;
  flex: 1;
}
.login-tip {
  font-size: 18px;
  color: #666;
  margin-bottom: 24px;
}
.empty-tip, .loading-tip {
  color: #999;
  font-size: 16px;
}
.empty-tip i, .loading-tip i {
  font-size: 48px;
  margin-bottom: 16px;
}
#chat-container {
  flex: 1;
  width: 100%;
  height: 100%;
  background: #f0f2f5;
}
.login-container {
  width: 320px;
  height: 380px;
  display: flex;
  align-items: center;
  justify-content: center;
}
</style>

<style>
#chat-container,
#chat-container > div,
#chat-container iframe {
  width: 100% !important;
  height: 100% !important;
  min-width: 100% !important;
  min-height: 100% !important;
  overflow: hidden;
}
</style>
