const nacl = require('tweetnacl');
const crypto = require('crypto');

class ClipsCrypto {
  constructor() {
    this.addressPrefix = 'CLIP-';
  }

  /**
   * Generate a new Ed25519 key pair
   * @returns {Object} Contains publicKey, privateKey, and address
   */
  generateKeyPair() {
    const keyPair = nacl.sign.keyPair();
    
    return {
      publicKey: Buffer.from(keyPair.publicKey).toString('hex'),
      privateKey: Buffer.from(keyPair.secretKey).toString('hex'),
      address: this.publicKeyToAddress(keyPair.publicKey)
    };
  }

  /**
   * Convert public key to CLIPS address format
   * @param {Uint8Array|Buffer|string} publicKey - The public key
   * @returns {string} CLIPS address
   */
  publicKeyToAddress(publicKey) {
    let pubKeyBuffer;
    
    if (typeof publicKey === 'string') {
      pubKeyBuffer = Buffer.from(publicKey, 'hex');
    } else if (publicKey instanceof Uint8Array) {
      pubKeyBuffer = Buffer.from(publicKey);
    } else {
      pubKeyBuffer = publicKey;
    }
    
    return this.addressPrefix + pubKeyBuffer.toString('hex').toUpperCase();
  }

  /**
   * Validate CLIPS address format
   * @param {string} address - The address to validate
   * @returns {boolean} True if valid
   */
  isValidAddress(address) {
    if (!address || typeof address !== 'string') {
      return false;
    }
    
    // Check prefix
    if (!address.startsWith(this.addressPrefix)) {
      return false;
    }
    
    // Check hex part (should be 64 characters after prefix)
    const hexPart = address.slice(this.addressPrefix.length);
    if (hexPart.length !== 64) {
      return false;
    }
    
    // Check if hex is valid
    return /^[0-9A-Fa-f]+$/.test(hexPart);
  }

  /**
   * Sign a transaction
   * @param {string} privateKeyHex - The private key in hex format
   * @param {Object} transaction - Transaction data
   * @returns {string} Signature in hex format
   */
  signTransaction(privateKeyHex, transaction) {
    const privateKey = Buffer.from(privateKeyHex, 'hex');
    
    // Create transaction message to sign
    const txMessage = this.createTransactionMessage(transaction);
    const messageBuffer = Buffer.from(txMessage, 'utf8');
    
    // Sign the message
    const signature = nacl.sign.detached(messageBuffer, privateKey);
    
    return Buffer.from(signature).toString('hex');
  }

  /**
   * Create transaction message for signing
   * @param {Object} tx - Transaction object
   * @returns {string} Message to sign
   */
  createTransactionMessage(tx) {
    // Format: sender:receiver:amount:nonce:type:data
    const parts = [
      tx.sender || '',
      tx.receiver || '',
      tx.amount || '0',
      tx.nonce || '0',
      tx.type || 'transfer',
      tx.data || ''
    ];
    
    return parts.join(':');
  }

  /**
   * Create a complete transaction object
   * @param {Object} params - Transaction parameters
   * @returns {Object} Complete transaction object
   */
  createTransaction(params) {
    const {
      sender,
      receiver,
      amount,
      nonce,
      type = 'transfer',
      data = '',
      privateKey
    } = params;

    const transaction = {
      sender,
      receiver,
      amount: parseInt(amount),
      nonce: parseInt(nonce),
      type,
      data,
      timestamp: Date.now()
    };

    // Sign the transaction
    if (privateKey) {
      transaction.signature = this.signTransaction(privateKey, transaction);
    }

    return transaction;
  }

  /**
   * Verify a transaction signature
   * @param {Object} transaction - The transaction object
   * @param {string} publicKeyHex - The public key to verify against
   * @returns {boolean} True if signature is valid
   */
  verifyTransaction(transaction, publicKeyHex) {
    if (!transaction.signature) {
      return false;
    }

    try {
      const publicKey = Buffer.from(publicKeyHex, 'hex');
      const signature = Buffer.from(transaction.signature, 'hex');
      
      // Recreate the message that was signed
      const txMessage = this.createTransactionMessage(transaction);
      const messageBuffer = Buffer.from(txMessage, 'utf8');
      
      return nacl.sign.detached.verify(messageBuffer, signature, publicKey);
    } catch (error) {
      console.error('Error verifying transaction:', error);
      return false;
    }
  }

  /**
   * Convert transaction to hex string for broadcasting
   * @param {Object} transaction - The transaction object
   * @returns {string} Transaction as hex string
   */
  transactionToHex(transaction) {
    const txString = JSON.stringify(transaction);
    return Buffer.from(txString, 'utf8').toString('hex');
  }

  /**
   * Convert hex string back to transaction object
   * @param {string} txHex - Transaction in hex format
   * @returns {Object} Transaction object
   */
  hexToTransaction(txHex) {
    const txString = Buffer.from(txHex, 'hex').toString('utf8');
    return JSON.parse(txString);
  }

  /**
   * Import private key and return key pair info
   * @param {string} privateKeyHex - Private key in hex format
   * @returns {Object} Key pair information
   */
  importPrivateKey(privateKeyHex) {
    try {
      const privateKey = Buffer.from(privateKeyHex, 'hex');
      
      // Generate public key from private key
      const keyPair = nacl.sign.keyPair.fromSecretKey(privateKey);
      
      return {
        publicKey: Buffer.from(keyPair.publicKey).toString('hex'),
        privateKey: privateKeyHex,
        address: this.publicKeyToAddress(keyPair.publicKey)
      };
    } catch (error) {
      throw new Error('Invalid private key format');
    }
  }

  /**
   * Generate a random seed phrase (12 words)
   * @returns {string} Seed phrase
   */
  generateSeedPhrase() {
    // Simple word list for demo purposes
    const wordList = [
      'abandon', 'ability', 'able', 'about', 'above', 'absent', 'absorb', 'abstract',
      'absurd', 'abuse', 'access', 'accident', 'account', 'accuse', 'achieve', 'acid',
      'acoustic', 'acquire', 'across', 'act', 'action', 'actor', 'actress', 'actual',
      'adapt', 'add', 'addict', 'address', 'adjust', 'admit', 'adult', 'advance',
      'advice', 'aerobic', 'affair', 'afford', 'afraid', 'again', 'against', 'agent',
      'agree', 'ahead', 'aim', 'air', 'airport', 'aisle', 'alarm', 'album',
      'alcohol', 'alert', 'alien', 'all', 'alley', 'allow', 'almost', 'alone',
      'alpha', 'already', 'also', 'alter', 'always', 'amateur', 'amazing', 'among',
      'amount', 'amused', 'analyst', 'anchor', 'ancient', 'anger', 'angle', 'angry',
      'animal', 'ankle', 'announce', 'annual', 'another', 'answer', 'antenna', 'antique'
    ];

    const words = [];
    for (let i = 0; i < 12; i++) {
      const randomIndex = crypto.randomInt(0, wordList.length);
      words.push(wordList[randomIndex]);
    }

    return words.join(' ');
  }
}

module.exports = new ClipsCrypto();