import bsv from '@ts-bitcoin/core';

type TXOBitcoinAddress = string;
type TXOTXHash = string;

export interface TXO {
  blk?: {
    i: number;
    h: string;
    t: any;
  };
  tx: {
    h: TXOTXHash;
    r: any;
  };
  in: TXOInput[];
  out: TXOOutput[];
  coinbase: any;
}

export interface TXOInput {
  i: number;
  b0: any;
  b1: any;
  b2: any;
  b3: any;
  s1: any;
  s2: any;
  s3: any;
  s4: any;
  h1: any;
  h2: any;
  h3: any;
  h4: any;
  e: {
    h: string;
    i: number;
    a: TXOBitcoinAddress;
    p: string;
  };
}

export interface TXOOutput {
  i: number;
  str: string;
  b0: any;
  b1: any;
  b2: any;
  b3: any;
  s1: any;
  s2: any;
  s3: any;
  s4: any;
  s5: any;
  s6: any;
  h1: any;
  h2: any;
  h3: any;
  h4: any;
  e: {
    v: number;
    i: number;
    a: TXOBitcoinAddress;
  };
}

export function rawTxToTxo(transaction: string, options?: any): TXO {
  const gene = bsv.Tx.fromHex(transaction);
  //   const t = gene.toObject();
  const inputs: any = [];
  const outputs: any = [];
  if (gene.txIns) {
    gene.txIns.forEach(function (input, input_index) {
      if (input.script) {
        const xput: any = { i: input_index, seq: input.nSequence };
        input.script.chunks.forEach(function (c, chunk_index) {
          if (c.buf) {
            if (c.buf.byteLength >= 1000000) {
              xput['xlb' + chunk_index] = c.buf.toString('base64');
            } else if (c.buf.byteLength >= 512 && c.buf.byteLength < 1000000) {
              xput['lb' + chunk_index] = c.buf.toString('base64');
            } else {
              xput['b' + chunk_index] = c.buf.toString('base64');
            }
            if (options && options.h && options.h > 0) {
              xput['h' + chunk_index] = c.buf.toString('hex');
            }
          } else {
            if (typeof c.opCodeNum !== 'undefined') {
              xput['b' + chunk_index] = {
                op: c.opCodeNum,
              };
            } else {
              xput['b' + chunk_index] = c;
            }
          }
        });
        const sender: any = {
          h: input.txHashBuf.toString('hex'),
          i: input.txOutNum,
        };
        // const address = input.script.toAddress(bsv.Networks.livenet.name).toString();
        const pubkey = input.script.chunks[1].buf;
        // const hashpubkey = bsv.Hash.sha256Ripemd160(pubkey!);
        const address = bsv.Address.fromPubKey(bsv.PubKey.fromBuffer(pubkey!));

        if (address && address.toString().length > 0) {
          sender.a = address.toString();
          sender.p = pubkey?.toString('hex');
        }

        xput.e = sender;
        inputs.push(xput);
      }
    });
  }
  if (gene.txOuts) {
    gene.txOuts.forEach(function (output, output_index) {
      if (output.script) {
        const xput: any = { i: output_index };
        output.script.chunks.forEach(function (c, chunk_index) {
          if (c.buf) {
            if (c.buf.byteLength >= 1000000) {
              xput['xlb' + chunk_index] = c.buf.toString('base64');
              xput['xls' + chunk_index] = c.buf.toString('utf8');
            } else if (c.buf.byteLength >= 512 && c.buf.byteLength < 1000000) {
              xput['lb' + chunk_index] = c.buf.toString('base64');
              xput['ls' + chunk_index] = c.buf.toString('utf8');
            } else {
              xput['b' + chunk_index] = c.buf.toString('base64');
              xput['s' + chunk_index] = c.buf.toString('utf8');
            }
            if (options && options.h && options.h > 0) {
              xput['h' + chunk_index] = c.buf.toString('hex');
            }
          } else {
            if (typeof c.opCodeNum !== 'undefined') {
              xput['b' + chunk_index] = {
                op: c.opCodeNum,
              };
            } else {
              xput['b' + chunk_index] = c;
            }
          }
        });
        const receiver = {
          v: output.valueBn.toNumber(),
          i: output_index,
          a: '',
        };
        if (isPublicKeyHashOut(output.script)) {
          const address = bsv.Address.fromPubKeyHashBuf(output.script.chunks[2].buf!);
          receiver.a = address.toString();
        }

        xput.e = receiver;
        outputs.push(xput);
      }
    });
  }
  const r: any = {
    tx: { h: gene.hash().toString('utf-8') },
    in: inputs,
    out: outputs,
    lock: gene.nLockTime,
  };
  // confirmations
  if (options && options.confirmations) {
    r.confirmations = options.confirmations;
  }
  return r;
}

function isPublicKeyHashOut(script: bsv.Script) {
  return !!(
    script.chunks.length === 5 &&
    script.chunks[0].opCodeNum === bsv.OpCode.OP_DUP &&
    script.chunks[1].opCodeNum === bsv.OpCode.OP_HASH160 &&
    script.chunks[2].buf &&
    script.chunks[2].buf.length === 20 &&
    script.chunks[3].opCodeNum === bsv.OpCode.OP_EQUALVERIFY &&
    script.chunks[4].opCodeNum === bsv.OpCode.OP_CHECKSIG
  );
}
