import { ref, reactive, watchPostEffect, onMounted, onUnmounted } from "vue";
import { defineStore } from "pinia";
import { useLocalStorage } from "@vueuse/core";
import { isString, toNumber, isUndefined, type Dictionary } from "lodash";
import z from "zod";
import { isNumber } from "mathjs";
import { Mutex, withTimeout } from "async-mutex";
import { useConstraintsStore } from "@/stores/constraints";
import { useDialogStore } from "./dialog";
import { toFileParameterOrUndefined } from "@/utils/Common";
import { AuthManager } from "@/utils/AuthManager";
import { HubConnection, HubConnectionBuilder } from "@microsoft/signalr";
import {
  getHubProxyFactory,
  getReceiverRegister,
} from "@/utils/signalR/TypedSignalR.Client";
import type { ResourceInfo } from "@/APIClient";
import type { IJtagHub } from "@/utils/signalR/TypedSignalR.Client/server.Hubs";

export const useEquipments = defineStore("equipments", () => {
  // Global Stores
  const constrainsts = useConstraintsStore();
  const dialog = useDialogStore();

  const boardAddr = useLocalStorage("fpga-board-addr", "127.0.0.1");
  const boardPort = useLocalStorage("fpga-board-port", 1234);

  // Jtag
  const jtagBitstream = ref<File>();
  const jtagBoundaryScanFreq = ref(100);
  const jtagUserBitstreams = ref<ResourceInfo[]>([]);
  const jtagClientMutex = withTimeout(
    new Mutex(),
    1000,
    new Error("JtagClient Mutex Timeout!"),
  );
  const jtagHubConnection = ref<HubConnection>();
  const jtagHubProxy = ref<IJtagHub>();

  onMounted(async () => {
    // 每次挂载都重新创建连接
    jtagHubConnection.value =
      AuthManager.createAuthenticatedJtagHubConnection();
    jtagHubProxy.value = getHubProxyFactory("IJtagHub").createHubProxy(
      jtagHubConnection.value,
    );

    getReceiverRegister("IJtagReceiver").register(jtagHubConnection.value, {
      onReceiveBoundaryScanData: async (msg) => {
        constrainsts.batchSetConstraintStates(msg);
      },
    });
    await jtagHubConnection.value.start();
  });

  onUnmounted(() => {
    // 断开连接，清理资源
    if (jtagHubConnection.value) {
      jtagHubConnection.value.stop();
      jtagHubConnection.value = undefined;
      jtagHubProxy.value = undefined;
    }
  });

  // Matrix Key
  const matrixKeyStates = reactive(new Array<boolean>(16).fill(false));
  const matrixKeypadClientMutex = withTimeout(
    new Mutex(),
    1000,
    new Error("Matrixkeyclient Mutex Timeout!"),
  );

  // Power
  const powerClientMutex = withTimeout(
    new Mutex(),
    1000,
    new Error("Matrixkeyclient Mutex Timeout!"),
  );

  // Enable Setting
  const enableJtagBoundaryScan = ref(false);
  const enableMatrixKey = ref(false);
  const enablePower = ref(false);

  function setMatrixKey(
    keyNum: number | string | undefined,
    keyValue: boolean,
  ): boolean {
    let _keyNum: number;
    if (isString(keyNum)) {
      _keyNum = toNumber(keyNum);
    } else if (isNumber(keyNum)) {
      _keyNum = keyNum;
    } else {
      return false;
    }

    if (z.number().nonnegative().max(16).safeParse(_keyNum).success) {
      matrixKeyStates[_keyNum] = keyValue;
      return true;
    }
    return false;
  }

  async function jtagBoundaryScanSetOnOff(enable: boolean) {
    if (isUndefined(jtagHubProxy.value)) {
      console.error("JtagHub Not Initialize...");
      return;
    }

    if (enable) {
      const ret = await jtagHubProxy.value.startBoundaryScan(
        jtagBoundaryScanFreq.value,
      );
      if (!ret) {
        console.error("Failed to start boundary scan");
        return;
      }
    } else {
      const ret = await jtagHubProxy.value.stopBoundaryScan();
      if (!ret) {
        console.error("Failed to stop boundary scan");
        return;
      }
    }
    enableJtagBoundaryScan.value = enable;
  }

  async function jtagUploadBitstream(
    bitstream: File,
    examId?: string,
  ): Promise<string | null> {
    try {
      // 自动开启电源
      await powerSetOnOff(true);

      const resourceClient = AuthManager.createAuthenticatedResourceClient();
      const resp = await resourceClient.addResource(
        "bitstream",
        "user",
        examId || null,
        toFileParameterOrUndefined(bitstream),
      );

      // 如果上传成功，设置为当前选中的比特流
      if (resp && resp.id !== undefined && resp.id !== null) {
        return resp.id;
      }

      return null;
    } catch (e) {
      dialog.error("上传错误");
      console.error(e);
      return null;
    }
  }

  async function jtagDownloadBitstream(bitstreamId?: string): Promise<string> {
    if (bitstreamId === null || bitstreamId === undefined) {
      dialog.error("请先选择要下载的比特流");
      return "";
    }

    const release = await jtagClientMutex.acquire();
    try {
      // 自动开启电源
      await powerSetOnOff(true);

      const jtagClient = AuthManager.createAuthenticatedJtagClient();
      const resp = await jtagClient.downloadBitstream(
        boardAddr.value,
        boardPort.value,
        bitstreamId,
      );
      return resp;
    } catch (e) {
      dialog.error("下载错误");
      console.error(e);
      throw e;
    } finally {
      release();
    }
  }

  async function jtagGetIDCode(isQuiet: boolean = false): Promise<number> {
    const release = await jtagClientMutex.acquire();
    try {
      // 自动开启电源
      await powerSetOnOff(true);

      const jtagClient = AuthManager.createAuthenticatedJtagClient();
      const resp = await jtagClient.getDeviceIDCode(
        boardAddr.value,
        boardPort.value,
      );
      return resp;
    } catch (e) {
      if (!isQuiet) dialog.error("获取IDCode错误");
      return 0xffff_ffff;
    } finally {
      release();
    }
  }

  async function jtagSetSpeed(speed: number): Promise<boolean> {
    const release = await jtagClientMutex.acquire();
    try {
      // 自动开启电源
      await powerSetOnOff(true);

      const jtagClient = AuthManager.createAuthenticatedJtagClient();
      const resp = await jtagClient.setSpeed(
        boardAddr.value,
        boardPort.value,
        speed,
      );
      return resp;
    } catch (e) {
      dialog.error("设置Jtag速度失败");
      return false;
    } finally {
      release();
    }
  }

  async function matrixKeypadSetKeyStates(keyStates: boolean[]) {
    const release = await matrixKeypadClientMutex.acquire();
    console.log("set Key !!!!!!!!!!!!");
    try {
      const matrixKeypadClient =
        AuthManager.createAuthenticatedMatrixKeyClient();
      const resp = await matrixKeypadClient.setMatrixKeyStatus(
        boardAddr.value,
        boardPort.value,
        keyStates,
      );
      return resp;
    } catch (e) {
      dialog.error("设置矩阵键盘时，服务器发生错误");
      return false;
    } finally {
      release();
    }
  }

  async function matrixKeypadEnable(enable: boolean) {
    const release = await matrixKeypadClientMutex.acquire();
    try {
      if (enable) {
        const matrixKeypadClient =
          AuthManager.createAuthenticatedMatrixKeyClient();
        const resp = await matrixKeypadClient.enabelMatrixKey(
          boardAddr.value,
          boardPort.value,
        );
        enableMatrixKey.value = resp;
        return resp;
      } else {
        const matrixKeypadClient =
          AuthManager.createAuthenticatedMatrixKeyClient();
        const resp = await matrixKeypadClient.disableMatrixKey(
          boardAddr.value,
          boardPort.value,
        );
        enableMatrixKey.value = !resp;
        return resp;
      }
    } catch (e) {
      enableMatrixKey.value = false;
      dialog.error("设置矩阵键盘是否启用时，服务器发生错误");
      return false;
    } finally {
      release();
    }
  }

  async function powerSetOnOff(enable: boolean) {
    const release = await powerClientMutex.acquire();
    try {
      const powerClient = AuthManager.createAuthenticatedPowerClient();
      const resp = await powerClient.setPowerOnOff(
        boardAddr.value,
        boardPort.value,
        enable,
      );
      return resp;
    } catch (e) {
      dialog.error("无法开关电源");
      console.error(e);
      return false;
    } finally {
      release();
    }
  }

  return {
    boardAddr,
    boardPort,
    setMatrixKey,

    // Jtag
    enableJtagBoundaryScan,
    jtagBoundaryScanSetOnOff,
    jtagBitstream,
    jtagBoundaryScanFreq,
    jtagUserBitstreams,
    jtagUploadBitstream,
    jtagDownloadBitstream,
    jtagGetIDCode,
    jtagSetSpeed,

    // Matrix Key
    enableMatrixKey,
    matrixKeyStates,
    matrixKeypadClientMutex,
    matrixKeypadEnable,
    matrixKeypadSetKeyStates,

    // Power
    enablePower,
    powerClientMutex,
    powerSetOnOff,
  };
});
