import { Command } from 'commander';
import {
  createNode,
  importKeyPair,
  createEnvelope,
  signEnvelope,
  createMessageRouter,
  createDHTOperations,
  sign,
  verify,
  extractPublicKey,
} from '@highway1/core';
import { getIdentity, getBootstrapPeers } from '../config.js';
import { success, error, spinner, printHeader, info } from '../ui.js';

export function registerSendCommand(program: Command): void {
  program
    .command('send')
    .description('Send a message to another agent')
    .requiredOption('--to <did>', 'Recipient DID')
    .requiredOption('--protocol <protocol>', 'Protocol identifier')
    .requiredOption('--payload <json>', 'Message payload (JSON)')
    .option('--type <type>', 'Message type (request|notification)', 'request')
    .option('--bootstrap <peers...>', 'Bootstrap peer addresses')
    .option('--peer <multiaddr>', 'Direct peer multiaddr (bypasses DHT lookup)')
    .action(async (options) => {
      try {
        printHeader('Send Message');

        const identity = getIdentity();

        if (!identity) {
          error('No identity found. Run "hw1 init" first.');
          process.exit(1);
        }

        let payload;
        try {
          payload = JSON.parse(options.payload);
        } catch {
          error('Invalid JSON payload');
          process.exit(1);
        }

        const spin = spinner('Starting node...');

        const keyPair = importKeyPair({
          publicKey: identity.publicKey,
          privateKey: identity.privateKey,
        });

        const bootstrapPeers = options.bootstrap || getBootstrapPeers();

        const node = await createNode({
          keyPair,
          bootstrapPeers,
          enableDHT: true,
        });

        // Register peer:identify listener BEFORE start() so we don't miss the event
        const identifyDone = new Promise<void>((resolve) => {
          const timeout = setTimeout(resolve, 12000);
          node.libp2p.addEventListener('peer:identify', () => {
            clearTimeout(timeout);
            setTimeout(resolve, 500);
          }, { once: true });
        });

        await node.start();

        spin.text = 'Connecting to network...';
        await identifyDone;

        const dht = createDHTOperations(node.libp2p);
        const verifyFn = async (signature: Uint8Array, data: Uint8Array): Promise<boolean> => {
          try {
            const decoded = JSON.parse(new TextDecoder().decode(data)) as { from?: string };
            if (!decoded.from || typeof decoded.from !== 'string') return false;
            const senderPublicKey = extractPublicKey(decoded.from);
            return verify(signature, data, senderPublicKey);
          } catch {
            return false;
          }
        };

        const router = createMessageRouter(
          node.libp2p,
          verifyFn,
          dht,
          bootstrapPeers
        );

        await router.start();

        spin.text = 'Creating message...';

        const envelope = createEnvelope(
          identity.did,
          options.to,
          options.type,
          options.protocol,
          payload
        );

        const signedEnvelope = await signEnvelope(envelope, (data) =>
          sign(data, keyPair.privateKey)
        );

        spin.text = 'Sending message...';

        // Build peer hint from --peer option if provided
        let peerHint = undefined;
        if (options.peer) {
          // Extract peerId from multiaddr (last component after /p2p/)
          const parts = options.peer.split('/p2p/');
          if (parts.length === 2) {
            peerHint = {
              peerId: parts[1],
              multiaddrs: [options.peer],
            };
            info(`Direct peer: ${options.peer}`);
          }
        }

        const response = await router.sendMessage(signedEnvelope, peerHint);

        spin.succeed('Message sent successfully!');

        console.log();
        info(`Message ID: ${signedEnvelope.id}`);
        info(`To: ${options.to}`);
        info(`Protocol: ${options.protocol}`);
        info(`Type: ${options.type}`);
        info(`Payload: ${JSON.stringify(payload)}`);

        // Display response if received
        if (response) {
          console.log();
          success('>>> Received response from recipient');
          info(`Response ID: ${response.id}`);
          info(`Reply To: ${response.replyTo}`);
          info(`Protocol: ${response.protocol}`);
          info(`Payload: ${JSON.stringify(response.payload, null, 2)}`);
        } else if (options.type === 'request') {
          console.log();
          info('No response received (recipient may not have returned a response)');
        }

        await router.stop();
        await node.stop();

        success('Done');
      } catch (err) {
        error(`Failed to send message: ${(err as Error).message}`);
        if ((err as Error).cause) error(`Cause: ${((err as Error).cause as Error).message}`);
        console.error(err);
        process.exit(1);
      }
    });
}
