import Signature from "../../crypto/signature";
import PublicKey from "../../keys/publickey";
import Script from "../script/script";
import ValidationUtils from "../../utils/validation.utils";
import SighashType from "./sighashtype";
import CommonUtils from "../../utils/common.utils";

export interface ITxSignature {
  inputIndex: number;
  publicKey: PublicKey | string;
  subscript: Script | string;
  signature: Signature | string;
  sigType: SighashType | string;
}

export default class TxSignature implements ITxSignature {

  public inputIndex: number;
  public publicKey: PublicKey;
  public subscript: Script;
  public signature: Signature;
  public sigType: SighashType;

  /**
   * Wrapper around Signature with fields related to signing a transaction specifically
   */
  constructor(arg: ITxSignature) {
    TxSignature._validateArgs(arg);

    this.inputIndex = arg.inputIndex;
    this.publicKey = typeof arg.publicKey === 'string' ? PublicKey.fromString(arg.publicKey) : arg.publicKey;
    this.subscript = typeof arg.subscript === 'string' ? Script.fromHex(arg.subscript) : arg.subscript;
    this.signature = typeof arg.signature === 'string' ? Signature.fromString(arg.signature) : arg.signature;
    this.sigType = typeof arg.sigType === 'string' ? SighashType.fromString(arg.sigType) : arg.sigType;
  }

  private static _validateArgs(arg: ITxSignature): void {
    ValidationUtils.validateArgument(typeof arg === 'object' && arg !== null, 'TxSignature must be instantiated from an object');
    ValidationUtils.validateArgument(arg.publicKey != null && !!PublicKey.from(arg.publicKey), 'publicKey');
    ValidationUtils.validateArgument(arg.inputIndex != null, 'inputIndex');
    ValidationUtils.validateState(typeof arg.inputIndex === 'number', 'inputIndex must be a number');
    ValidationUtils.validateArgument(arg.subscript != null, 'subscript');
    ValidationUtils.validateState(arg.subscript instanceof Script || CommonUtils.isHexa(arg.subscript!), 'subscript must be an object or hexa value');
    ValidationUtils.validateArgument(arg.signature != null, 'signature');
    ValidationUtils.validateState(arg.signature instanceof Signature || CommonUtils.isHexa(arg.signature!), 'signature must be an object or hexa value');
    ValidationUtils.validateState(arg.sigType instanceof SighashType || typeof arg.sigType === 'string', 'sigtype must be a sigtype object or string');
  }

  public toJSON = this.toObject;

  public toObject(): ITxSignature {
    return {
      inputIndex: this.inputIndex,
      publicKey: this.publicKey.toString(),
      subscript: this.subscript.toHex(),
      signature: this.signature.toString(),
      sigType: this.sigType.toString()
    }
  }

  public static fromObject(arg: ITxSignature): TxSignature {
    return new TxSignature(arg);
  }

  public toTxSatisfier(): Uint8Array {
    return this.signature.toTxFormat(this.sigType.toBuffer());
  }
}