import {
  Central,
  ScannedPeripheral,
  ConnectionStatus,
} from "@systemic-games/react-native-bluetooth-le";

const ftmsServiceUuid = "1826";
const ftmsIndoorBikeDataUuid = "2ad2";

const fullUUID = (uuid: string) =>
  uuid.length === 4
    ? `0000${uuid}-0000-1000-8000-00805f9b34fb`
    : uuid;

export type FtmsIndoorBikeData = {
  speedKph: number;
  cadence: number;
  power: number;
  raw: Uint8Array;
};

type Listener = (data: FtmsIndoorBikeData) => void;

type SubscriptionEntry = {
  listeners: Set<Listener>;
  lastData: FtmsIndoorBikeData | null;
  subscribePromise: Promise<void> | null;
  subscribed: boolean;
  cadenceState: {
    lastCadenceValue: number;
    lastCadenceChangedTime: number;
  };
  lastEmitTime: number; // ✅ throttle 用
};

const entries = new Map<string, SubscriptionEntry>();
let connectionListenerInstalled = false;

const getDeviceKey = (peripheral: ScannedPeripheral) =>
  String(peripheral.address || peripheral.systemId || peripheral.name || "unknown");

const resetEntrySubscriptionState = (
  key: string,
  connectionStatus?: ConnectionStatus
) => {
  const entry = entries.get(key);
  if (!entry) return;

  entry.subscribed = false;
  entry.subscribePromise = null;
  entry.lastEmitTime = 0;
  entry.cadenceState.lastCadenceValue = 0;
  entry.cadenceState.lastCadenceChangedTime = 0;

  if (connectionStatus !== "connected" && connectionStatus !== "ready") {
    entry.lastData = null;
  }
};

const ensureConnectionListenerInstalled = () => {
  if (connectionListenerInstalled) return;

  Central.addListener("peripheralConnectionStatus", (ev) => {
    const key = getDeviceKey(ev.peripheral);

    if (
      ev.connectionStatus === "disconnected" ||
      ev.connectionStatus === "disconnecting" ||
      ev.connectionStatus === "connecting"
    ) {
      resetEntrySubscriptionState(key, ev.connectionStatus);
    }
  });

  connectionListenerInstalled = true;
};

// ✅ 默认关闭调试（避免性能问题）
const DEBUG = false;

const bytesToHex = (bytes: Uint8Array) =>
  Array.from(bytes)
    .map((b) => b.toString(16).padStart(2, "0").toUpperCase())
    .join("");

const decodeBase64 = (value: string): Uint8Array | null => {
  try {
    const atobFn = (globalThis as { atob?: (data: string) => string }).atob;

    if (typeof atobFn === "function") {
      const binary = atobFn(value);
      const bytes = new Uint8Array(binary.length);

      for (let i = 0; i < binary.length; i += 1) {
        bytes[i] = binary.charCodeAt(i);
      }

      return bytes;
    }

    const chars =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    let buffer = 0;
    let bits = 0;
    const output: number[] = [];

    for (const char of value.replace(/=+$/, "")) {
      const index = chars.indexOf(char);
      if (index < 0) continue;

      buffer = (buffer << 6) | index;
      bits += 6;

      if (bits >= 8) {
        bits -= 8;
        output.push((buffer >> bits) & 0xff);
      }
    }

    return new Uint8Array(output);
  } catch (err) {
    if (DEBUG) console.warn("[FTMS] decodeBase64 failed", err);
    return null;
  }
};

const toUint8Array = (raw: any): Uint8Array | null => {
  try {
    if (!raw) return null;

    if (raw instanceof Uint8Array) return raw;
    if (raw instanceof ArrayBuffer) return new Uint8Array(raw);
    if (typeof raw === "string") return decodeBase64(raw);

    if (Array.isArray(raw)) return new Uint8Array(raw);

    if (raw?.buffer instanceof ArrayBuffer) {
      return new Uint8Array(raw.buffer, raw.byteOffset || 0, raw.byteLength);
    }

    return null;
  } catch (err) {
    if (DEBUG) console.warn("[FTMS] toUint8Array failed", err);
    return null;
  }
};

const parseIndoorBikeData = (
  byteArray: Uint8Array,
  cadenceState: SubscriptionEntry["cadenceState"]
): FtmsIndoorBikeData | null => {
  if (byteArray.length < 8) return null;

  const currentTime = Date.now();
  const rawSpeed = byteArray[2] | (byteArray[3] << 8);
  const rawCadence = byteArray[4] | (byteArray[5] << 8);
  let rawPower = byteArray[6] | (byteArray[7] << 8);

  const speedKph = Number((rawSpeed / 100.0).toFixed(1));
  const cadenceValue = rawCadence / 2.0;

  if (cadenceValue !== cadenceState.lastCadenceValue) {
    cadenceState.lastCadenceChangedTime = currentTime;
    cadenceState.lastCadenceValue = cadenceValue;
  }

  const cadence =
    cadenceState.lastCadenceChangedTime > 0 &&
    currentTime - cadenceState.lastCadenceChangedTime > 3000
      ? 0
      : Math.round(cadenceValue);

  if (rawPower & 0x8000) {
    rawPower -= 0x10000;
  }

  const power = rawPower;

  return {
    speedKph,
    cadence,
    power,
    raw: byteArray,
  };
};

const ensureSubscribed = async (
  peripheral: ScannedPeripheral,
  entry: SubscriptionEntry
) => {
  if (entry.subscribed) return;

  if (entry.subscribePromise) {
    await entry.subscribePromise;
    return;
  }

  entry.subscribePromise = Central.subscribeCharacteristic(
    peripheral,
    fullUUID(ftmsServiceUuid),
    fullUUID(ftmsIndoorBikeDataUuid),
    (notifyEv) => {
      try {
        const byteArray = toUint8Array(notifyEv.value);
        if (!byteArray || byteArray.length === 0) return;

        if (DEBUG) {
          const flags =
            byteArray.length >= 2
              ? byteArray[0] | (byteArray[1] << 8)
              : 0;

          console.log("[FTMS]", bytesToHex(byteArray));
          console.log(
            "[FTMS] flags =",
            "0x" + flags.toString(16).padStart(4, "0")
          );
        }

        const parsed = parseIndoorBikeData(
          byteArray,
          entry.cadenceState
        );
        if (!parsed) return;

        const now = Date.now();

        // ✅ throttle: 限制为 10Hz（100ms 一次）
        if (now - entry.lastEmitTime < 100) {
          return;
        }

        entry.lastEmitTime = now;

        if (DEBUG) {
          console.log(
            "[FTMS] parsed =>",
            parsed.speedKph,
            parsed.cadence,
            parsed.power
          );
        }

        entry.lastData = parsed;
        entry.listeners.forEach((listener) => listener(parsed));
      } catch (err) {
        if (DEBUG) console.warn("处理 2AD2 通知失败", err);
      }
    }
  )
    .then(() => {
      entry.subscribed = true;
      if (DEBUG) console.log("[FTMS] subscribed");
    })
    .catch((err) => {
      if (DEBUG) console.warn("[FTMS] subscribe failed", err);
      throw err;
    })
    .finally(() => {
      entry.subscribePromise = null;
    });

  await entry.subscribePromise;
};

export const observeFtmsIndoorBikeData = async (
  peripheral: ScannedPeripheral,
  listener: Listener
) => {
  ensureConnectionListenerInstalled();

  const key = getDeviceKey(peripheral);
  let entry = entries.get(key);

  if (!entry) {
    entry = {
      listeners: new Set(),
      lastData: null,
      subscribePromise: null,
      subscribed: false,
      cadenceState: {
        lastCadenceValue: 0,
        lastCadenceChangedTime: 0,
      },
      lastEmitTime: 0, // ✅ 初始化
    };
    entries.set(key, entry);
  }

  if (entry.listeners.size === 0) {
    resetEntrySubscriptionState(key, "disconnected");
  }

  entry.listeners.add(listener);
  await ensureSubscribed(peripheral, entry);

  if (entry.lastData) {
    listener(entry.lastData);
  }

  return () => {
    const currentEntry = entries.get(key);
    if (!currentEntry) return;
    currentEntry.listeners.delete(listener);
  };
};

export const debugFtmsIndoorBikeDataEntries = () => {
  return Array.from(entries.entries()).map(([key, entry]) => ({
    key,
    listeners: entry.listeners.size,
    subscribed: entry.subscribed,
    lastDataHex: entry.lastData ? bytesToHex(entry.lastData.raw) : null,
  }));
};
