import TcpSocket from 'react-native-tcp-socket';
import { TemFrameParser } from '../protocol/parser';
import type { RawFrame } from '../protocol/types';

export type TcpCallbacks = {
  onConnect: () => void;
  onFrame: (frame: RawFrame) => void;
  onError: (err: Error) => void;
  onClose: () => void;
};

class TcpService {
  private socket: ReturnType<typeof TcpSocket.createConnection> | null = null;
  private parser = new TemFrameParser();
  private callbacks: TcpCallbacks | null = null;
  private connected = false;
  private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
  private heartbeatTimer: ReturnType<typeof setInterval> | null = null;
  private reconnectEnabled = false;
  private host = '';
  private port = 0;

  connect(host: string, port: number, callbacks: TcpCallbacks) {
    this.disconnect();
    this.host = host;
    this.port = port;
    this.callbacks = callbacks;
    this.reconnectEnabled = true;
    this.parser.reset();
    this.createSocket();
  }

  private createSocket() {
    if (this.socket) {
      this.socket.destroy();
      this.socket = null;
    }

    if (__DEV__) console.log(`[TCP] creating socket → ${this.host}:${this.port}`);
    const connectTimeout = setTimeout(() => {
      if (!this.connected && sock) {
        sock.destroy();
        this.callbacks?.onError(new Error('连接超时 (10s)'));
        this.scheduleReconnect();
      }
    }, 10000);

    const sock = TcpSocket.createConnection(
      { host: this.host, port: this.port, tls: false },
      () => {
        clearTimeout(connectTimeout);
        this.connected = true;
        clearTimeout(this.reconnectTimer!);
        this.startHeartbeat();
        if (__DEV__) console.log(`[TCP] ✓ connected to ${this.host}:${this.port}`);
        this.callbacks?.onConnect();
      },
    );

    sock.on('data', (data: any) => {
      if (__DEV__) console.log('[TCP] data event fired, type:', typeof data, 'constructor:', data?.constructor?.name, 'length:', data?.length ?? data?.byteLength ?? '?');
      let arr: Uint8Array;
      if (data instanceof Uint8Array) {
        arr = data;
      } else if (data instanceof ArrayBuffer) {
        arr = new Uint8Array(data);
      } else if (typeof data === 'string') {
        // react-native-tcp-socket may deliver data as a UTF-8 string
        const bytes = new Uint8Array(data.length);
        for (let i = 0; i < data.length; i++) bytes[i] = data.charCodeAt(i) & 0xff;
        arr = bytes;
      } else if (data?.buffer) {
        arr = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
      } else {
        if (__DEV__) console.warn('[TCP] unknown data type:', typeof data, data);
        return;
      }
      if (__DEV__) console.log('[TCP] raw recv', arr.length, 'bytes:', Array.from(arr.slice(0, 20)).map(b => '0x' + b.toString(16).padStart(2, '0')).join(' '));
      const frames = this.parser.feed(arr);
      if (__DEV__) console.log('[TCP] parsed frames:', frames.length);
      frames.forEach((f) => this.callbacks?.onFrame(f));
    });

    sock.on('error', (err: Error) => {
      if (__DEV__) console.warn('[TCP] error event:', err.message);
      this.stopHeartbeat();
      this.connected = false;
      this.callbacks?.onError(err);
      this.scheduleReconnect();
    });

    sock.on('close', () => {
      if (__DEV__) console.log('[TCP] close event');
      this.stopHeartbeat();
      this.connected = false;
      this.callbacks?.onClose();
      this.scheduleReconnect();
    });

    this.socket = sock;
  }

  private startHeartbeat() {
    this.stopHeartbeat();
    this.heartbeatTimer = setInterval(() => {
      if (this.connected && this.socket) {
        try {
          this.socket.write('0');
        } catch {
          this.connected = false;
          this.stopHeartbeat();
          this.callbacks?.onError(new Error('Heartbeat write failed'));
          this.scheduleReconnect();
        }
      }
    }, 8000);
  }

  private stopHeartbeat() {
    if (this.heartbeatTimer) {
      clearInterval(this.heartbeatTimer);
      this.heartbeatTimer = null;
    }
  }

  private scheduleReconnect() {
    if (!this.reconnectEnabled) return;
    clearTimeout(this.reconnectTimer!);
    this.reconnectTimer = setTimeout(() => {
      if (this.reconnectEnabled) {
        this.parser.reset();
        this.createSocket();
      }
    }, 3000);
  }

  send(data: Uint8Array): boolean {
    if (!this.connected || !this.socket) return false;
    try {
      if (__DEV__) console.log('[TCP] send', data.length, 'bytes:', Array.from(data.slice(0, 16)).map(b => '0x' + b.toString(16).padStart(2, '0')).join(' '));
      this.socket.write(data as unknown as string);
      return true;
    } catch (e) {
      if (__DEV__) console.warn('[TCP] send error:', e);
      return false;
    }
  }

  disconnect() {
    this.reconnectEnabled = false;
    this.stopHeartbeat();
    clearTimeout(this.reconnectTimer!);
    this.socket?.destroy();
    this.socket = null;
    this.connected = false;
    this.parser.reset();
  }

  isConnected() {
    return this.connected;
  }

  resetParser() {
    this.parser.reset();
  }
}

// Singleton instance shared across the app
export const tcpService = new TcpService();
