import { useWebSocket } from '@vueuse/core';
import { Ref, unref } from 'vue';
import { DataBoardLayoutInfo } from '../types/type';
import { JWT_TOKEN_KEY } from '/@/enums/cacheEnum';
import { useGlobSetting } from '/@/hooks/setting';
import { getAuthCache } from '/@/utils/auth';
import { isNullAndUnDef } from '/@/utils/is';

interface SocketMessage {
  tsSubCmds: SocketMessageItem[];
}

interface SocketMessageItem {
  entityType: string;
  entityId: string;
  scope: string;
  cmdId: number;
  keys: string;
}

interface GroupMappingRecord {
  id: string;
  recordIndex: number;
  dataSourceIndex: number;
  attribute: string;
  deviceId: string;
  slaveDeviceId: string;
}

interface ResponseMessage {
  subscriptionId: number;
  errorCode: number;
  errorMsg: Nullable<string>;
  data: {
    [key: string]: [[number, string]];
  };
  latestValues: {
    [key: string]: number;
  };
}

const generateMessage = (deviceId: string, cmdId: number, attr: string): SocketMessageItem => {
  return {
    entityType: 'DEVICE',
    entityId: deviceId,
    scope: 'LATEST_TELEMETRY',
    cmdId,
    keys: attr,
  };
};

export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) {
  const token = getAuthCache(JWT_TOKEN_KEY);

  const cmdIdMapping = new Map<number, GroupMappingRecord[]>();

  const groupMapping = new Map<string, GroupMappingRecord[]>();

  const waitSendQueue: string[] = [];

  const { socketUrl } = useGlobSetting();

  const config = {
    server: `${socketUrl}${token}`,
  };

  // const getNeedUpdateValueById = (componentId: string, deviceId: string) => {};

  const getNeedUpdateValueByIndex = (recordIndex: number, dataSourceIndex: number) => {
    return unref(dataSourceRef)[recordIndex].record.dataSource[dataSourceIndex];
  };

  const mergeGroup = (dataSourceRef: Ref<DataBoardLayoutInfo[]>) => {
    for (let recordIndex = 0; recordIndex < unref(dataSourceRef).length; recordIndex++) {
      const record = unref(dataSourceRef).at(recordIndex)!;
      const dataSource = record?.record.dataSource;
      for (let dataSourceIndex = 0; dataSourceIndex < dataSource.length; dataSourceIndex++) {
        const dataDatum = dataSource.at(dataSourceIndex)!;
        const { deviceId, slaveDeviceId, attribute } = dataDatum;
        const groupMappingRecord: GroupMappingRecord = {
          id: record.record.id,
          recordIndex,
          dataSourceIndex,
          attribute,
          deviceId,
          slaveDeviceId,
        };
        if (groupMapping.has(slaveDeviceId || deviceId)) {
          const group = groupMapping.get(slaveDeviceId || deviceId);
          group?.push(groupMappingRecord);
        } else {
          groupMapping.set(slaveDeviceId || deviceId, [groupMappingRecord]);
        }
      }
    }
  };

  function generateGroupMessage() {
    const messageList: SocketMessageItem[] = [];
    let cmdId = 0;
    groupMapping.forEach((value, key) => {
      const message = generateMessage(
        key,
        cmdId,
        Array.from(new Set(value.map((item) => item.attribute))).join(',')
      );
      messageList.push(message);
      setCmdId(cmdId, value);
      cmdId++;
    });
    return messageList;
  }

  const { close, send, open, status } = useWebSocket(config.server, {
    onConnected() {
      if (waitSendQueue.length) {
        waitSendQueue.forEach((string) => {
          send(string);
        });
        waitSendQueue.length = 0;
      }
    },
    onMessage(_ws, message) {
      try {
        const res: ResponseMessage = JSON.parse(message.data);
        const { subscriptionId, data = {} } = res;
        if (isNullAndUnDef(subscriptionId)) return;
        const mappingRecord = cmdIdMapping.get(subscriptionId);
        if (!mappingRecord) return;
        mappingRecord.forEach((item) => {
          const { attribute, recordIndex, dataSourceIndex } = item;
          const [[timespan, value]] = data[attribute];
          const record = getNeedUpdateValueByIndex(recordIndex, dataSourceIndex);
          record.componentInfo.value = value;
          record.componentInfo.updateTime = timespan;
        });
        return;
      } catch (error) {
        throw Error(error as string);
      }
    },
    // onDisconnected() {
    //   close();
    // },
  });

  const setCmdId = (cmdId: number, record: GroupMappingRecord[]) => {
    cmdIdMapping.set(cmdId, record);
  };

  const transformSocketMessageItem = () => {
    mergeGroup(dataSourceRef);
    return {
      tsSubCmds: generateGroupMessage(),
    } as SocketMessage;
  };

  const beginSendMessage = () => {
    close();
    cmdIdMapping.clear();

    // TODO current need use setTimeout delay 1 second to reconnect
    setTimeout(() => {
      open();
      const messageList = transformSocketMessageItem();

      if (unref(status) !== 'OPEN') {
        waitSendQueue.push(JSON.stringify(messageList));
        return;
      }
      send(JSON.stringify(messageList));
    }, 1000);
  };

  return {
    close,
    send,
    open,
    beginSendMessage,
  };
}
