import { bytesToHex } from '@noble/hashes/utils.js';
import { Packet } from '../protocols/meshcore';
import type { Payload } from '../protocols/meshcore.types';
import { BaseStream } from './Stream';

export interface MeshCoreMessage {
  topic: string;
  receivedAt: Date;
  raw: Uint8Array;
  hash: string;
  decodedPayload?: Payload;
  radioName?: string;
}

interface MeshCoreJsonEnvelope {
  payloadBase64?: string;
  payloadHex?: string;
}

const hexToBytes = (hex: string): Uint8Array => {
  const normalized = hex.trim().toLowerCase();
  if (normalized.length % 2 !== 0) {
    throw new Error('MeshCoreStream: invalid hex payload length');
  }

  const bytes = new Uint8Array(normalized.length / 2);
  for (let i = 0; i < normalized.length; i += 2) {
    bytes[i / 2] = Number.parseInt(normalized.slice(i, i + 2), 16);
  }
  return bytes;
};

export class MeshCoreStream extends BaseStream {
  constructor(autoConnect = false) {
    super({}, autoConnect);
  }

  protected decodeMessage(topic: string, payload: Uint8Array): MeshCoreMessage {
    const packetBytes = this.extractPacketBytes(payload);
    const parsed = new Packet();
    parsed.parse(packetBytes);

    let decodedPayload: Payload | undefined;
    try {
      decodedPayload = parsed.decode();
    } catch {
      decodedPayload = undefined;
    }

    console.log('parsed packet', parsed, { decodedPayload });

    // Extract radio name from topic: meshcore/packet/<base64-encoded-radio-name>
    const radioName = this.extractRadioNameFromTopic(topic);

    return {
      topic,
      receivedAt: new Date(),
      raw: packetBytes,
      hash: bytesToHex(parsed.hash()),
      decodedPayload,
      radioName
    };
  }

  private extractRadioNameFromTopic(topic: string): string | undefined {
    // Topic format: meshcore/packet/<base64-encoded-radio-name>
    const parts = topic.split('/');
    if (parts.length >= 3 && parts[0] === 'meshcore' && parts[1] === 'packet') {
      try {
        const base64Name = parts[2];
        return atob(base64Name);
      } catch (error) {
        console.warn('Failed to decode radio name from topic:', topic, error);
        return undefined;
      }
    }
    return undefined;
  }

  private extractPacketBytes(payload: Uint8Array): Uint8Array {
    const text = new TextDecoder().decode(payload).trim();
    if (!text.startsWith('{')) {
      return payload;
    }

    const envelope = JSON.parse(text) as MeshCoreJsonEnvelope;
    if (envelope.payloadBase64) {
      return Uint8Array.from(atob(envelope.payloadBase64), (c) => c.charCodeAt(0));
    }

    if (envelope.payloadHex) {
      return hexToBytes(envelope.payloadHex);
    }

    return payload;
  }
}
