// src/utils/socket.ts
import type { SocketOptions, HeartbeatData } from "@renderer/types/socket";

/**
 * WebSocket 长连接工具类（TS 版）
 * @class SocketClient
 * @param {string} url - WS/WSS 连接地址
 * @param {SocketOptions} options - 配置项
 */
class SocketClient {
  private url: string; // 连接地址（私有属性）
  private ws: WebSocket | null; // WebSocket 实例
  private isConnected: boolean; // 连接状态
  private heartbeatTimer: NodeJS.Timeout | null; // 心跳定时器
  private reconnectTimer: NodeJS.Timeout | null; // 重连定时器
  private reconnectCount: number; // 已重连次数
  private config: Required<SocketOptions>; // 完整配置（包含默认值）

  constructor(url: string, options: SocketOptions = {}) {
    this.url = url;
    this.ws = null;
    this.isConnected = false;
    this.heartbeatTimer = null;
    this.reconnectTimer = null;
    this.reconnectCount = 0;

    // 合并默认配置与用户配置（确保所有配置项有值）
    this.config = {
      heartbeatInterval: options.heartbeatInterval || 30000,
      reconnectInterval: options.reconnectInterval || 3000,
      maxReconnectCount: options.maxReconnectCount || 10,
      onMessage: options.onMessage || (() => {}),
      onError: options.onError || (() => {}),
      onClose: options.onClose || (() => {}),
      onOpen: options.onOpen || (() => {})
    };

    // 初始化连接
    this.init();
  }

  /**
   * 初始化 WebSocket 连接
   */
  private init(): void {
    // 关闭已有连接
    this.close();

    try {
      this.ws = new WebSocket(this.url);

      // 连接成功
      this.ws.onopen = (): void => {
        this.isConnected = true;
        this.reconnectCount = 0;
        this.config.onOpen();
        this.startHeartbeat();
      };

      // 接收消息
      this.ws.onmessage = (event: MessageEvent): void => {
        this.config.onMessage(event.data);
      };

      // 错误回调
      this.ws.onerror = (error: Event): void => {
        this.isConnected = false;
        this.config.onError(error);
      };

      // 关闭回调
      this.ws.onclose = (event: CloseEvent): void => {
        this.isConnected = false;
        this.config.onClose(event);
        this.stopHeartbeat();

        // 自动重连（未超过最大次数）
        if (this.reconnectCount < this.config.maxReconnectCount) {
          this.reconnect();
        }
      };
    } catch (error) {
      this.config.onError(error as Event);
      this.reconnect();
    }
  }

  /**
   * 启动心跳保活
   */
  private startHeartbeat(): void {
    this.stopHeartbeat();

    this.heartbeatTimer = setInterval(() => {
      if (this.isConnected && this.ws) {
        // 发送心跳包（强类型）
        const heartbeatData: HeartbeatData = {
          type: 'ping',
          timestamp: Date.now()
        };
        this.send(heartbeatData);
      }
    }, this.config.heartbeatInterval);
  }

  /**
   * 停止心跳
   */
  private stopHeartbeat(): void {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
  }

  /**
   * 发送消息
   * @param {string | object} data - 要发送的消息（字符串/对象）
   * @throws {Error} 连接未建立时抛出错误
   */
  public send(data: string | object): void {
    if (!this.isConnected || !this.ws) {
      throw new Error('WebSocket 未连接，无法发送消息');
    }

    // 类型判断：对象转为 JSON 字符串
    const sendData: string = typeof data === 'object' 
      ? JSON.stringify(data) 
      : data;

    this.ws.send(sendData);
  }

  /**
   * 断线重连（阶梯式间隔）
   */
  private reconnect(): void {
    this.reconnectCount++;
    const currentInterval = this.config.reconnectInterval * this.reconnectCount;

    this.reconnectTimer = setTimeout(() => {
      console.log(`WebSocket 第 ${this.reconnectCount} 次重连...`);
      this.init();
    }, currentInterval);
  }

  /**
   * 主动关闭连接
   * @param {number} code - 关闭码（默认 1000：正常关闭）
   * @param {string} reason - 关闭原因
   */
  public close(code: number = 1000, reason: string = '主动关闭连接'): void {
    if (this.ws) {
      this.ws.close(code, reason);
      this.ws = null;
    }
    this.isConnected = false;
    this.stopHeartbeat();

    if (this.reconnectTimer) {
      clearTimeout(this.reconnectTimer);
      this.reconnectTimer = null;
    }
  }

  /**
   * 获取当前连接状态
   * @returns {boolean} 连接状态
   */
  public getConnectedState(): boolean {
    return this.isConnected;
  }
}

export default SocketClient;