import React from 'react';
import { bytesToHex } from '@noble/hashes/utils.js';

import { Card, Stack } from 'react-bootstrap';

import PacketDissectionViewer from '../../components/PacketDissectionViewer';
import type { Segment } from '../../types/protocol/dissection.types';

import {
  payloadNameByValue,
  routeDisplayByValue,
} from './MeshCoreData';
import { PayloadType, type AdvertPayload, type AnonReqPayload, type DecryptedGroupMessage, type GroupTextPayload, type Payload } from '../../types/protocol/meshcore.types';
import type { Packet } from '../../protocols/meshcore';

const HeaderFact: React.FC<{ label: string; value: React.ReactNode }> = ({ label, value }) => (
  <div className="meshcore-fact-row">
    <span className="meshcore-fact-label">{label}</span>
    <span className="meshcore-fact-value">{value}</span>
  </div>
);

const buildMeshCoreSegments = (packet: Packet): Segment[] => {
  const { pathHashSize, pathHashCount } = packet;
  const pathLength = pathHashSize * pathHashCount;

  const segments: Segment[] = [
    {
      name: 'Header',
      offset: 0,
      byteCount: 1,
      bitfields: [
        {
          offset: 6,
          length: 2,
          name: 'Version',
        },
        {
          offset: 2,
          length: 4,
          name: `Payload Type (${payloadNameByValue[packet.payloadType] ?? 'Unknown'})`,
        },
        {
          offset: 0,
          length: 2,
          name: `Route Type (${routeDisplayByValue[packet.routeType] ?? 'Unknown'})`,
        },
      ],
    },
    {
      name: 'Path Descriptor',
      offset: 1,
      byteCount: 1,
      bitfields: [
        {
          offset: 6,
          length: 2,
          name: `Hash Size Selector (size=${pathHashSize})`,
        },
        {
          offset: 0,
          length: 6,
          name: `Hash Count (count=${pathHashCount})`,
        },
      ],
    },
  ];

  if (pathLength > 0) {
    segments.push({
      name: 'Path Data',
      offset: 2,
      byteCount: pathLength,
      attributes: [
        {
          byteWidth: pathLength,
          type: 'bytes',
          name: `Path Hashes (${pathHashCount} × ${pathHashSize} bytes)`,
        },
      ],
    });
  }

  const payloadOffset = 2 + (packet.transportCodes ? packet.transportCodes.length * 2 : 0) + pathLength;
  if (packet.payload.length > 0) {
    segments.push({
      name: 'Payload',
      offset: payloadOffset,
      byteCount: packet.payload.length,
      attributes: [
        {
          byteWidth: packet.payload.length,
          type: 'bytes',
          name: `Payload Data (${packet.payload.length} bytes)`,
        },
      ],
    });
  }

  if (packet.segments) {
    segments.push(...packet.segments);
  }

  return segments;
};

const asRecord = (value: unknown): Record<string, unknown> | null => {
  if (value && typeof value === 'object') {
    return value as Record<string, unknown>;
  }
  return null;
};

const PayloadDetails: React.FC<{ packet: Packet }> = ({ packet }) => {
  const payload = packet.decodedPayload as Payload | undefined;
  const payloadObj = asRecord(payload);

  if (typeof payload === 'undefined' || !payload || !payloadObj) {
    return (
      <Card body className="data-table-card">
        <h6 className="mb-2">Payload</h6>
        <div>Unable to decode payload; showing raw bytes only.</div>
        <code>{bytesToHex(packet.payload)}</code>
      </Card>
    );
  }

  if (typeof payloadObj?.flags === 'number' && payloadObj?.data instanceof Uint8Array) {
    return (
      <Card body className="data-table-card">
        <h6 className="mb-2">Control</h6>
        <HeaderFact label="Flags" value={`0x${payloadObj.flags.toString(16)}`} />
        <HeaderFact label="Data Length" value={payloadObj.data.length} />
        <HeaderFact label="Data" value={<code>{bytesToHex(payloadObj.data)}</code>} />
      </Card>
    );
  }

  if (packet.payloadType === PayloadType.GROUP_TEXT && typeof packet.decrypted !== 'undefined') {
    const payload = packet.decodedPayload as GroupTextPayload;
    const decrypted = packet.decrypted as DecryptedGroupMessage;
    return (
      <Card body className="data-table-card">
        <h6 className="mb-2">{payloadNameByValue[packet.payloadType] ?? packet.payloadType} (Encrypted)</h6>
        <HeaderFact label="Channel Hash" value={<><span className="meshcore-hash">{payload.channelHash}</span>{decrypted.group ? ` (${decrypted.group})` : ''}</>} />
        <HeaderFact label="Timestamp" value={decrypted.timestamp.toLocaleString()} />
        <HeaderFact label="Message" value={decrypted.message} />
      </Card>
    );
  }
  if (
    typeof payloadObj.channelHash === 'string'
    && payloadObj.cipherText instanceof Uint8Array
    && payloadObj.cipherMAC instanceof Uint8Array
  ) {
    return (
      <Card body className="data-table-card">
        <h6 className="mb-2">{payloadNameByValue[packet.payloadType] ?? packet.payloadType} (Encrypted)</h6>
        <HeaderFact label="Channel Hash" value={payloadObj.channelHash} />
        <HeaderFact label="Cipher Text" value={payloadObj.cipherText.length + ' bytes'} />
        <HeaderFact label="Cipher MAC" value={<code>{bytesToHex(payloadObj.cipherMAC)}</code>} />
      </Card>
    );
  }

  if (
    typeof payloadObj.dstHash === 'string'
    && typeof payloadObj.srcHash === 'string'
    && payloadObj.cipherText instanceof Uint8Array
    && payloadObj.cipherMAC instanceof Uint8Array
  ) {
    return (
      <Card body className="data-table-card">
        <h6 className="mb-2">{payloadNameByValue[packet.payloadType] ?? packet.payloadType} (Encrypted)</h6>
        <HeaderFact label="Destination" value={<span className="meshcore-hash">{payloadObj.dstHash}</span>} />
        <HeaderFact label="Source" value={<span className="meshcore-hash">{payloadObj.srcHash}</span>} />
        <HeaderFact label="Cipher Text Length" value={payloadObj.cipherText.length} />
        <HeaderFact label="Cipher MAC" value={<code>{bytesToHex(payloadObj.cipherMAC)}</code>} />
      </Card>
    );
  }

  if (payloadObj.data instanceof Uint8Array) {
    return (
      <Card body className="data-table-card">
        <h6 className="mb-2">{payloadNameByValue[packet.payloadType] ?? packet.payloadType} (Raw)</h6>
        <HeaderFact label="Data Length" value={payloadObj.data.length} />
        <HeaderFact label="Data" value={<code>{bytesToHex(payloadObj.data)}</code>} />
      </Card>
    );
  }

  if (packet.payloadType === PayloadType.ADVERT) {
    const advert = payload as AdvertPayload;
    return (
      <Card body className="data-table-card">
        <h6 className="mb-2">Advert Payload</h6>
        <HeaderFact label="Public Key" value={<code>{bytesToHex(advert.publicKey)}</code>} />
        <HeaderFact label="Signature" value={advert.signature.length + ' Bytes'} />
        {advert.timestamp && (
          <HeaderFact label="Time" value={<code>{advert.timestamp.toISOString()}</code>} />
        )}
        {advert.appdata?.name && (
          <HeaderFact label="Name" value={advert.appdata.name} />
        )}
        {advert.appdata?.latitude && (
          <HeaderFact label="Latitude" value={advert.appdata.latitude} />
        )}
        {advert.appdata?.longitude && (
          <HeaderFact label="Longitude" value={advert.appdata.longitude} />
        )}
      </Card>
    );
  }

  if (packet.payloadType === PayloadType.ANON_REQ) {
    const anonReq = payload as AnonReqPayload;
      return (
        <Card body className="data-table-card">
          <h6 className="mb-2">Anonymous Request</h6>
          <HeaderFact label="Destination" value={anonReq.dstHash} />
          <HeaderFact label="Public Key" value={<code>{bytesToHex(anonReq.publicKey)}</code>} />
        </Card>
      );
  }

  return (
    <Card body className="data-table-card">
      <h6 className="mb-2">Payload</h6>
      <code>{JSON.stringify(payloadObj)}</code>
    </Card>
  );
};

interface MeshCorePacketDetailsPaneProps {
  packet: Packet | null;
  streamReady: boolean;
}

const MeshCorePacketDetailsPane: React.FC<MeshCorePacketDetailsPaneProps> = ({ packet, streamReady }) => {
  if (!packet) {
    return (
      <Card body className="data-table-card h-100">
        <h6>Select a packet</h6>
        <div>Click any hash in the table to inspect MeshCore header and payload details.</div>
        <div className="mt-2 text-secondary">Stream prepared: {streamReady ? 'yes' : 'no'}</div>
      </Card>
    );
  }

  return (
    <Stack gap={2} className="h-100 meshcore-detail-stack">
      {/*
      <Card body className="data-table-card">
        <Stack direction="horizontal" gap={2} className="mb-2">
          <h6 className="mb-0">{payloadNameByValue[packet.payloadType] ?? packet.payloadType}</h6>
        </Stack>
        <HeaderFact label="Time" value={packet.timestamp.toISOString()} />
        <HeaderFact label="Hash" value={<code>{packet.hash}</code>} />
        {packet.snr !== undefined && <HeaderFact label="SNR" value={`${packet.snr.toFixed(1)} dB`} />}
        {packet.rssi !== undefined && <HeaderFact label="RSSI" value={`${packet.rssi} dBm`} />}
        {packet.radioName && <HeaderFact label="Radio" value={packet.radioName} />}
        <HeaderFact label="Version" value={packet.version} />
        <HeaderFact label="Payload Type" value={`${payloadNameByValue[packet.payloadType] ?? 'Unknown'} (${packet.payloadType})`} />
        <HeaderFact label="Route Type" value={routeDisplayByValue[packet.routeType] ?? packet.routeType} />
        <HeaderFact label="Raw Length" value={`${packet.raw.length} bytes`} />
        <HeaderFact label="Path" value={<code>{bytesToHex(packet.path)}</code>} />
        <HeaderFact label="Raw Packet" value={<code>{bytesToHex(packet.raw)}</code>} />
      </Card>
      */}
      <PayloadDetails packet={packet} />
      <Card body className="data-table-card">
        <PacketDissectionViewer
          rawPacket={packet.toBytes()}
          segments={packet.segments || buildMeshCoreSegments(packet)}
          title={`${payloadNameByValue[packet.payloadType] ?? packet.payloadType} Packet Dissection`}
        />
      </Card>
      <Card body className="data-table-card">
        <h6 className="mb-2">Stream Preparation</h6>
        <div>MeshCore stream service is initialized and ready for topic subscriptions.</div>
        <div className="text-secondary">Ready: {streamReady ? 'yes' : 'no'}</div>
      </Card>
    </Stack>
  );
};

export default MeshCorePacketDetailsPane;
