import access from '@ohos.bluetooth.access';
import connection from '@ohos.bluetooth.connection';
import socket from '@ohos.bluetooth.socket';
import { ExpenseItem } from './ExpenseModel';

export interface BluetoothDevice {
  deviceId: string;
  deviceName: string;
}

export type DeviceDiscoveredCallback = (device: BluetoothDevice) => void;
export type TransferProgressCallback = (current: number, total: number) => void;
export type TransferResultCallback = (success: boolean, data?: ExpenseItem[], error?: string) => void;

const SPP_UUID = '00001101-0000-1000-8000-00805F9B34FB';
const DATA_END_MARKER = '__BT_EOF__';

export class BluetoothService {
  private serverSocket: number = -1;
  private clientSocket: number = -1;
  private onDeviceFound: DeviceDiscoveredCallback | null = null;
  private destroyFlag: boolean = false;

  isBluetoothEnabled(): boolean {
    try {
      return access.getState() === access.BluetoothState.STATE_ON;
    } catch (_e) {
      return false;
    }
  }

  enableBluetooth(): void {
    if (!this.isBluetoothEnabled()) {
      try {
        access.enableBluetooth();
      } catch (_e) {
        // best effort
      }
    }
  }

  waitForBluetoothOn(timeoutMs: number): Promise<boolean> {
    if (access.getState() === access.BluetoothState.STATE_ON) {
      return Promise.resolve(true);
    }
    return new Promise<boolean>((resolve) => {
      let resolved = false;
      const timer = setTimeout(() => {
        if (!resolved) {
          resolved = true;
          try { access.off('stateChange', handler); } catch (_e) {}
          resolve(false);
        }
      }, timeoutMs);

      const handler = (data: access.BluetoothState) => {
        if (resolved) return;
        if (data === access.BluetoothState.STATE_ON) {
          resolved = true;
          clearTimeout(timer);
          try { access.off('stateChange', handler); } catch (_e) {}
          resolve(true);
        }
      };
      try {
        access.on('stateChange', handler);
      } catch (_e) {
        if (!resolved) {
          resolved = true;
          clearTimeout(timer);
          resolve(false);
        }
      }
    });
  }

  getLocalDeviceName(): string {
    try {
      return connection.getLocalName();
    } catch (_e) {
      return '我的设备';
    }
  }

  getPairedDevices(): BluetoothDevice[] {
    try {
      const list = connection.getPairedDevices();
      const result: BluetoothDevice[] = [];
      for (const addr of list) {
        if (!addr) continue;
        let name = '未知设备';
        try { name = connection.getRemoteDeviceName(addr); } catch (_e) {}
        result.push({ deviceId: addr, deviceName: name });
      }
      return result;
    } catch (_e) {
      return [];
    }
  }

  startDiscovery(onDevice: DeviceDiscoveredCallback): void {
    this.onDeviceFound = onDevice;
    this.destroyFlag = false;
    try {
      connection.on('bluetoothDeviceFind', (devices: string[]) => {
        if (this.destroyFlag) return;
        for (const addr of devices) {
          if (!addr) continue;
          let name = '未知设备';
          try { name = connection.getRemoteDeviceName(addr); } catch (_e) {}
          this.onDeviceFound?.({ deviceId: addr, deviceName: name });
        }
      });
      connection.startBluetoothDiscovery();
    } catch (e) {
      console.error('startDiscovery failed: ' + JSON.stringify(e));
    }
  }

  stopDiscovery(): void {
    this.destroyFlag = true;
    try {
      connection.stopBluetoothDiscovery();
      connection.off('bluetoothDeviceFind');
    } catch (_e) {}
    this.onDeviceFound = null;
  }

  startServer(
    onResult: TransferResultCallback,
    onProgress: TransferProgressCallback
  ): void {
    this.destroyFlag = false;
    try {
      const option: socket.SppOptions = {
        uuid: SPP_UUID,
        secure: true,
        type: socket.SppType.SPP_RFCOMM
      };
      socket.sppListen('记账传输', option, (err, serverId) => {
        if (this.destroyFlag) {
          this.closeAll();
          return;
        }
        if (err) {
          onResult(false, undefined, '启动监听失败: ' + JSON.stringify(err));
          return;
        }
        this.serverSocket = serverId;
        socket.sppAccept(serverId, (acceptErr, clientId) => {
          if (this.destroyFlag) {
            this.closeAll();
            return;
          }
          if (acceptErr) {
            onResult(false, undefined, '接受连接失败: ' + JSON.stringify(acceptErr));
            this.closeAll();
            return;
          }
          this.clientSocket = clientId;
          this.receiveDataLoop(onResult, onProgress);
        });
      });
    } catch (e) {
      if (!this.destroyFlag) {
        onResult(false, undefined, '启动监听失败: ' + JSON.stringify(e));
      }
    }
  }

  private receiveDataLoop(
    onResult: TransferResultCallback,
    onProgress: TransferProgressCallback
  ): void {
    const chunks: Uint8Array[] = [];
    let totalSize = 0;

    const readHandler = (data: ArrayBuffer) => {
      if (this.destroyFlag || this.clientSocket < 0) return;

      const chunk = new Uint8Array(data);
      chunks.push(chunk);
      totalSize += chunk.byteLength;

      const allData = this.concatChunks(chunks, totalSize);
      const text = this.bytesToStr(allData);
      const eofIdx = text.indexOf(DATA_END_MARKER);
      if (eofIdx >= 0) {
        const jsonStr = text.substring(0, eofIdx);
        try {
          const records = JSON.parse(jsonStr) as ExpenseItem[];
          onProgress(totalSize, totalSize);
          onResult(true, records);
        } catch (_e) {
          onResult(false, undefined, '数据解析失败');
        }
        this.closeAll();
        return;
      }
      onProgress(totalSize, totalSize + 1);
    };

    try {
      socket.on('sppRead', this.clientSocket, readHandler);
    } catch (e) {
      if (!this.destroyFlag) {
        onResult(false, undefined, '注册读取监听失败: ' + JSON.stringify(e));
      }
      this.closeAll();
    }
  }

  connectAndSend(
    deviceId: string,
    data: ExpenseItem[],
    onResult: TransferResultCallback,
    onProgress: TransferProgressCallback
  ): void {
    if (data.length === 0) {
      onResult(false, undefined, '没有可发送的数据');
      return;
    }
    this.destroyFlag = false;
    try {
      const option: socket.SppOptions = {
        uuid: SPP_UUID,
        secure: true,
        type: socket.SppType.SPP_RFCOMM
      };
      socket.sppConnect(deviceId, option, (err, clientId) => {
        if (this.destroyFlag) {
          this.closeAll();
          return;
        }
        if (err) {
          onResult(false, undefined, '连接失败: ' + JSON.stringify(err));
          this.closeAll();
          return;
        }
        this.clientSocket = clientId;
        this.sendData(data, onResult, onProgress);
      });
    } catch (e) {
      if (!this.destroyFlag) {
        onResult(false, undefined, '连接失败: ' + JSON.stringify(e));
      }
      this.closeAll();
    }
  }

  private sendData(
    data: ExpenseItem[],
    onResult: TransferResultCallback,
    onProgress: TransferProgressCallback
  ): void {
    try {
      const jsonStr = JSON.stringify(data);
      const payload = jsonStr + DATA_END_MARKER;
      const buffer = this.strToBytes(payload);
      const total = buffer.byteLength;
      const chunkSize = 512;
      let sent = 0;

      while (sent < total && !this.destroyFlag && this.clientSocket >= 0) {
        const end = Math.min(sent + chunkSize, total);
        const chunk = buffer.slice(sent, end);
        const writeBuf = chunk.buffer.slice(chunk.byteOffset, chunk.byteOffset + chunk.byteLength);
        socket.sppWrite(this.clientSocket, writeBuf);
        sent = end;
        onProgress(sent, total);
      }
      if (!this.destroyFlag) {
        onResult(true);
      }
    } catch (e) {
      if (!this.destroyFlag) {
        onResult(false, undefined, '发送失败: ' + JSON.stringify(e));
      }
    } finally {
      setTimeout(() => this.closeAll(), 300);
    }
  }

  stopServer(): void {
    this.destroyFlag = true;
    this.closeAll();
  }

  cancelTransfer(): void {
    this.destroyFlag = true;
    this.closeAll();
  }

  private closeAll(): void {
    try { socket.sppCloseClientSocket(this.clientSocket); } catch (_e) {}
    try { socket.sppCloseServerSocket(this.serverSocket); } catch (_e) {}
    this.clientSocket = -1;
    this.serverSocket = -1;
  }

  destroy(): void {
    this.destroyFlag = true;
    this.stopDiscovery();
    this.closeAll();
  }

  strToBytes(str: string): Uint8Array {
    const buf = new ArrayBuffer(str.length * 3);
    const view = new Uint8Array(buf);
    let pos = 0;
    for (let i = 0; i < str.length; i++) {
      const code = str.charCodeAt(i);
      if (code < 0x80) {
        view[pos++] = code;
      } else if (code < 0x800) {
        view[pos++] = 0xC0 | (code >> 6);
        view[pos++] = 0x80 | (code & 0x3F);
      } else {
        view[pos++] = 0xE0 | (code >> 12);
        view[pos++] = 0x80 | ((code >> 6) & 0x3F);
        view[pos++] = 0x80 | (code & 0x3F);
      }
    }
    return new Uint8Array(buf, 0, pos);
  }

  bytesToStr(arr: Uint8Array): string {
    let result = '';
    let i = 0;
    while (i < arr.length) {
      const b1 = arr[i++];
      if (b1 < 0x80) {
        result += String.fromCharCode(b1);
      } else if ((b1 & 0xE0) === 0xC0) {
        const b2 = arr[i++];
        result += String.fromCharCode(((b1 & 0x1F) << 6) | (b2 & 0x3F));
      } else {
        const b2 = arr[i++];
        const b3 = arr[i++];
        result += String.fromCharCode(((b1 & 0x0F) << 12) | ((b2 & 0x3F) << 6) | (b3 & 0x3F));
      }
    }
    return result;
  }

  private concatChunks(chunks: Uint8Array[], totalSize: number): Uint8Array {
    const result = new Uint8Array(totalSize);
    let offset = 0;
    for (const c of chunks) {
      result.set(c, offset);
      offset += c.byteLength;
    }
    return result;
  }
}

export const bluetoothService = new BluetoothService();
