<template>
  <div class="live-room-ws-client">
    <!-- WebSocket component for live room connections -->
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, onUnmounted, watch } from 'vue';
import { message } from 'ant-design-vue';
import { initCrypto, getSecretKeysConfig } from '#/crypto';

interface Props {
  roomId: number;
  token: string;
  enable?: boolean;
}

interface Emits {
  (e: 'connected', clientId: string): void;
  (e: 'disconnected', roomId: number): void;
  (e: 'message', message: any): void;
  (e: 'error', error: Error): void;
  (e: 'authFailed'): void;
}

const props = withDefaults(defineProps<Props>(), {
  enable: true
});

const emits = defineEmits<Emits>();

// WebSocket instance
const ws = ref<WebSocket | null>(null);

// Connection state
const state = reactive({
  isConnected: false,
  isAuthenticated: false,
  clientId: '',
  retryCount: 0,
  lastHeartbeat: 0,
  reconnectTimeout: null as number | null,
  heartbeatInterval: null as number | null,
  authTimeout: null as number | null
});

// Constants
const WS_URL = 'wss://ws.bmiss3.zhubei.cn/';
const MAX_RETRY_ATTEMPTS = 5;
const RETRY_BASE_DELAY = 1000; // 1 second
const RETRY_MAX_DELAY = 30000; // 30 seconds
const HEARTBEAT_INTERVAL = 40000; // 40 seconds (same as Java code)
const AUTH_TIMEOUT = 10000; // 10 seconds



// Build WebSocket URL with parameters (matching Java's buildWsUri method)
function buildWsUrl() {
  // 初始化加密工具
  const cryptoTool = initCrypto();
  
  // 1. Get base parameters
  const params: Record<string, string> = {
    device: 'wap',
    source: 'com.sanya.xinxiulive.lite',
    request_number: generateRequestNumber(),
    timestamp: Math.floor(Date.now() / 1000).toString(),
    appversion: '1.0.0',
    room_id: props.roomId.toString() // 添加直播间 id
  };

  // 2. Add token if present
  if (props.token) {
    params.token = props.token;
  }

  // 3. Get sign_name (matching Java's selectRandomSecretKey method)
  const secretKeys = getSecretKeysConfig();
  const secretKeyNames = Object.keys(secretKeys);
  const signName = secretKeyNames.length > 0 
    ? secretKeyNames[Math.floor(Math.random() * secretKeyNames.length)] 
    : 'default';
  
  if (signName) {
    params.sign_name = signName;
  }

  // 4. Generate signature (double MD5 as in Java code)
  const sortedKeys = Object.keys(params).sort();
  const signString = sortedKeys
    .filter(key => key !== 'signature') // Exclude signature itself
    .map(key => `${key}=${params[key]}`)
    .join('&');

  // Use crypto-js for proper MD5 hashing
  // Note: crypto-js should be installed in the project
  function md5Hash(str: string): string {
    try {
      // Import crypto-js dynamically if available
      const CryptoJS = require('crypto-js');
      return CryptoJS.MD5(str).toString();
    } catch (error) {
      // Fallback to simplified implementation
      console.warn('crypto-js not available, using simplified MD5 implementation');
      const encoder = new TextEncoder();
      const data = encoder.encode(str);
      return Array.from(data)
        .map(b => b.toString(16).padStart(2, '0'))
        .join('');
    }
  }

  // Double MD5 encryption
  const firstMd5 = md5Hash(signString);
  const secondMd5 = md5Hash(firstMd5);
  params.signature = secondMd5;

  // 5. Build query string
  const queryString = Object.entries(params)
    .map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(value)}`)
    .join('&');

  // 6. Assemble final URL
  const finalUrl = WS_URL + (WS_URL.includes('?') ? '&' : '?') + queryString;
  console.log(`[Room ${props.roomId}] Built WebSocket URL:`, finalUrl);
  return finalUrl;
}

// Generate request number (matching Java's generateRequestNumber method)
function generateRequestNumber() {
  return 'java' + Math.floor(10000 + Math.random() * 90000);
}

// Connect to WebSocket
function connect() {
  if (!props.enable) return;
  if (ws.value && ws.value.readyState === WebSocket.OPEN) return;
  // 检查 token 和 roomId 是否存在
  if (!props.token) {
    console.error('WebSocket connection failed: token is required');
    emits('error', new Error('token is required'));
    return;
  }
  if (!props.roomId || props.roomId === 0) {
    console.error('WebSocket connection failed: roomId is required');
    emits('error', new Error('roomId is required'));
    return;
  }

  try {
    // Reset state
    state.retryCount = 0;
    state.isConnected = false;
    state.isAuthenticated = false;

    // Build WebSocket URL with parameters
    const wsUrl = buildWsUrl();
    console.log(`[Room ${props.roomId}] Connecting to:`, wsUrl);

    // Create WebSocket connection
    ws.value = new WebSocket(wsUrl);

    // Event listeners
    ws.value.onopen = handleOpen;
    ws.value.onmessage = handleMessage;
    ws.value.onclose = handleClose;
    ws.value.onerror = handleError;

  } catch (error) {
    console.error('WebSocket connection error:', error);
    emits('error', error as Error);
    scheduleReconnect();
  }
}

// Handle WebSocket open event
function handleOpen() {
  console.log(`[Room ${props.roomId}] WebSocket connected`);
  state.isConnected = true;
  
  // Set auth timeout
  state.authTimeout = window.setTimeout(() => {
    if (!state.isAuthenticated) {
      console.warn(`[Room ${props.roomId}] Auth timeout, closing connection`);
      disconnect();
      scheduleReconnect();
    }
  }, AUTH_TIMEOUT);
}

// Handle WebSocket message event
function handleMessage(event: MessageEvent) {
  try {
    const message = JSON.parse(event.data);
    console.debug(`[Room ${props.roomId}] Received message:`, message);

    // Handle authentication response
    if (message.scene === 'single') {
      if (message.type === 'success') {
        handleAuthSuccess();
      } else if (message.type === 'fail_token') {
        handleAuthFail();
      } else if (message.type === 'client_id') {
        handleClientId(message.client_id);
      }
    }
    
    // Handle chat messages
    if (message.scene === 'live' && message.type === 'chat') {
      emits('message', message);
    }

  } catch (error) {
    console.error(`[Room ${props.roomId}] Message parsing error:`, error);
  }
}

// Handle authentication success
function handleAuthSuccess() {
  console.log(`[Room ${props.roomId}] Authentication successful`);
  state.isAuthenticated = true;
  
  // Clear auth timeout
  if (state.authTimeout) {
    clearTimeout(state.authTimeout);
    state.authTimeout = null;
  }
  
  // Start heartbeat
  startHeartbeat();
}

// Handle authentication failure
function handleAuthFail() {
  console.error(`[Room ${props.roomId}] Token authentication failed`);
  emits('authFailed');
  disconnect();
}

// Handle client ID received
function handleClientId(clientId: string) {
  console.log(`[Room ${props.roomId}] Received client ID:`, clientId);
  state.clientId = clientId;
  emits('connected', clientId);
}

// Handle WebSocket close event
function handleClose(event: CloseEvent) {
  console.log(`[Room ${props.roomId}] WebSocket closed:`, event.code, event.reason);
  state.isConnected = false;
  state.isAuthenticated = false;
  
  // Cleanup
  cleanup();
  
  // Emit disconnect event
  emits('disconnected', props.roomId);
  
  // Schedule reconnect if not manually closed
  if (props.enable) {
    scheduleReconnect();
  }
}

// Handle WebSocket error event
function handleError(event: Event) {
  console.error(`[Room ${props.roomId}] WebSocket error:`, event);
  emits('error', new Error('WebSocket connection error'));
}

// Start heartbeat mechanism
function startHeartbeat() {
  // Clear existing interval
  if (state.heartbeatInterval) {
    clearInterval(state.heartbeatInterval);
  }
  
  // Set new heartbeat interval
  state.heartbeatInterval = window.setInterval(() => {
    if (ws.value && ws.value.readyState === WebSocket.OPEN) {
      console.debug(`[Room ${props.roomId}] Sending heartbeat`);
      // Send ping (actual implementation depends on server requirements)
      // For now, we'll just track heartbeat time
      state.lastHeartbeat = Date.now();
    }
  }, HEARTBEAT_INTERVAL);
}

// Schedule reconnection with exponential backoff
function scheduleReconnect() {
  if (!props.enable) return;
  if (state.retryCount >= MAX_RETRY_ATTEMPTS) {
    console.error(`[Room ${props.roomId}] Max retry attempts reached, giving up`);
    return;
  }
  
  // Calculate delay with exponential backoff and jitter
  const attempts = state.retryCount + 1;
  let delay = RETRY_BASE_DELAY * Math.pow(2, Math.min(attempts, 10) - 1);
  delay = Math.min(delay, RETRY_MAX_DELAY);
  // Add jitter (±20%)
  const jitter = delay * 0.2;
  delay = delay + (Math.random() * jitter * 2 - jitter);
  delay = Math.max(delay, 1000); // Minimum 1 second
  
  console.log(`[Room ${props.roomId}] Scheduling reconnect in ${Math.round(delay)}ms (Attempt ${attempts})`);
  
  state.retryCount = attempts;
  
  if (state.reconnectTimeout) {
    clearTimeout(state.reconnectTimeout);
  }
  
  state.reconnectTimeout = window.setTimeout(() => {
    connect();
  }, delay);
}

// Disconnect WebSocket
function disconnect() {
  // Clear timeouts and intervals
  cleanup();
  
  // Close WebSocket connection
  if (ws.value) {
    ws.value.close();
    ws.value = null;
  }
  
  state.isConnected = false;
  state.isAuthenticated = false;
}

// Cleanup resources
function cleanup() {
  if (state.reconnectTimeout) {
    clearTimeout(state.reconnectTimeout);
    state.reconnectTimeout = null;
  }
  
  if (state.heartbeatInterval) {
    clearInterval(state.heartbeatInterval);
    state.heartbeatInterval = null;
  }
  
  if (state.authTimeout) {
    clearTimeout(state.authTimeout);
    state.authTimeout = null;
  }
}

// Watch for token changes
watch(() => props.token, (newToken) => {
  if (newToken) {
    if (state.isConnected) {
      // Reconnect with new token
      disconnect();
      connect();
    }
  }
});

// Watch for enable changes
watch(() => props.enable, (enabled) => {
  if (enabled) {
    connect();
  } else {
    disconnect();
  }
});

// Watch for roomId changes
watch(() => props.roomId, (newRoomId, oldRoomId) => {
  if (oldRoomId && newRoomId !== oldRoomId) {
    disconnect();
    connect();
  }
});

// Lifecycle cleanup
onUnmounted(() => {
  disconnect();
});

// Expose methods
defineExpose({
  connect,
  disconnect,
  sendMessage: (message: any) => {
    if (ws.value && ws.value.readyState === WebSocket.OPEN) {
      ws.value.send(JSON.stringify(message));
    }
  }
});
</script>

<style scoped>
.live-room-ws-client {
  display: none;
}
</style>